2017-09-14 74 views
1

我正在寻找一个标准的Python 2.7包提供一个HTTP服务器,同时执行连接在同一个端口号。Python 2.7:流HTTP服务器支持一个端口上的多个连接

嘿你是主持人那里,请停止标记我的问题作为想要以非流式方式提供服务的问题的副本,例如:Multithreaded web server in python。不,我不想要诸如ThreadingMixIn这样的黑客,它只收集响应并将其作为一个单元返回。

换句话说,我正在寻找标准的方式去做下面的例子程序 - 但是没有自己编写整个HTTP服务器。

import time, socket, threading 

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
host = socket.gethostname() 
port = 8000 

sock.bind((host, port)) 
sock.listen(1) 

# my OWN HTTP server... Oh man, this is bad style. 
HTTP = "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\n\n" 

class Listener(threading.Thread): 

    def __init__(self): 
     threading.Thread.__init__(self) 
     self.daemon = True # stop Python from biting ctrl-C 
     self.start() 

    def run(self): 
     conn, addr = sock.accept() 
     conn.send(HTTP) 

     # serve up an infinite stream 
     i = 0 
     while True: 
      conn.send("%i " % i) 
      time.sleep(0.1) 
      i += 1 

[Listener() for i in range(100)] 
time.sleep(9e9) 

所以首先我想:

# run with this command: 
# gunicorn -k gevent myapp:app 
import time 

def app(environ, start_response): 
    data = b"Hello, World!\n" 
    start_response("200 OK", [ 
     ("Content-Type", "text/plain"), 
     ("Content-Length", str(len(data))) 
    ]) 
    for i in range(5): 
     time.sleep(1) 
     yield "Hello %i\n" % i 

# https://stackoverflow.com/questions/22739394/streaming-with-gunicorn 

但遗憾的是它不流,甚至与-k gevent

更新:它似乎gunicorn正在尝试做Keepalive,这将需要与last-chunk位的块传输编码。快速的消息来源显示它没有实现这一点。所以我可能需要一个更有趣的HTTP服务器,或者一个更简单的HTTP服务器(就像我上面的第一个例子,基于socket),它不会影响keepalive(对于大数据流来说这非常愚蠢)。

于是我尝试:

import time 
import threading 

import BaseHTTPServer 

class Handler(BaseHTTPServer.BaseHTTPRequestHandler): 

    def do_GET(self): 
     if self.path != '/': 
      self.send_error(404, "Object not found") 
      return 
     self.send_response(200) 
     self.send_header('Content-type', 'text/html; charset=utf-8') 
     self.end_headers() 

     # serve up an infinite stream 
     i = 0 
     while True: 
      self.wfile.write("%i " % i) 
      time.sleep(0.1) 
      i += 1 

class Listener(threading.Thread): 

    def __init__(self, i): 
     threading.Thread.__init__(self) 
     self.i = i 
     self.daemon = True 
     self.start() 

    def run(self): 
     server_address = ('', 8000+self.i) # How to attach all of them to 8000? 
     httpd = BaseHTTPServer.HTTPServer(server_address, Handler) 
     httpd.serve_forever() 

[Listener(i) for i in range(100)] 
time.sleep(9e9) 

这是非常好的,但它是一个有点讨厌,我不得不分配100端口号。这将需要一个令人讨厌的客户端重定向来让浏览器进入下一个可用的端口(好吧,我可以用JavaScript隐藏它,但它不够优雅,我宁愿写自己的HTTP服务器也不会那样)。

必须有一个简单的方法才能在一个端口上获得所有BaseHTTPServer听众,因为它是设置Web服务器的一种标准方式。或者也许gunicorn或某些包可以制作可靠的流?

+0

看看[扭曲](https://twistedmatrix.com/) – aergistal

+0

@aergistal嗯,问候*网络*例如在该网站上是完全不流......如果你搜索该网站您可以获取实际基于BaseHTTPServer的https://twistedmatrix.com/trac/attachment/ticket/9187/server.py。因此,这只是更确认BaseHTTPServer是否可行。 –

回答

2

默认BaseHTTPServer设置在每个侦听器上重新绑定一个新的套接字,如果所有侦听器都在同一个端口上,这在Linux中将不起作用。更改BaseHTTPServer.HTTPServer()呼叫和serve_forever()呼叫之间的这些设置。

以下示例在同一端口上启动100个处理程序线程,每个处理程序通过BaseHTTPServer启动。

import time, threading, socket, SocketServer, BaseHTTPServer 

class Handler(BaseHTTPServer.BaseHTTPRequestHandler): 

    def do_GET(self): 
     if self.path != '/': 
      self.send_error(404, "Object not found") 
      return 
     self.send_response(200) 
     self.send_header('Content-type', 'text/html; charset=utf-8') 
     self.end_headers() 

     # serve up an infinite stream 
     i = 0 
     while True: 
      self.wfile.write("%i " % i) 
      time.sleep(0.1) 
      i += 1 

# Create ONE socket. 
addr = ('', 8000) 
sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
sock.bind(addr) 
sock.listen(5) 

# Launch 100 listener threads. 
class Thread(threading.Thread): 
    def __init__(self, i): 
     threading.Thread.__init__(self) 
     self.i = i 
     self.daemon = True 
     self.start() 
    def run(self): 
     httpd = BaseHTTPServer.HTTPServer(addr, Handler, False) 

     # Prevent the HTTP server from re-binding every handler. 
     # https://stackoverflow.com/questions/46210672/ 
     httpd.socket = sock 
     httpd.server_bind = self.server_close = lambda self: None 

     httpd.serve_forever() 
[Thread(i) for i in range(100)] 
time.sleep(9e9) 
+0

这对我来说很棒。我只是在BaseHTTPServer中重写了所有烧瓶/ gunicorn/gevent应用程序(甚至是非流式应用程序),吞吐量双倍增加!好东西,我有我自己的URL装饰器在底部,使这一个简单的变化。 –

+0

更新:通过上面的代码,我已经运行了数百万个SSL连接(在任何给定时间同时达到2000个),没有任何错误(在我的测试中,我使连接有限并减少了休眠时间来强调系统)。(在启动线程之前,SSL很容易通过'sock = ssl.SSLSocket(sock,...)'添加) –

相关问题