代码之家  ›  专栏  ›  技术社区  ›  personal_cloud

Python 2.7:在一个端口上支持多个连接的流式HTTP服务器

  •  9
  • personal_cloud  · 技术社区  · 7 年前

    我正在寻找一个标准的Python 2.7包,提供一个HTTP服务器,可以同时执行 相同端口号上的连接。

    Multithreaded web server in python . 不,我不想要像这样的黑客 ThreadingMixIn 它只是收集响应并将其作为一个单元返回。

    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 .

    last-chunk 一点快速对消息来源进行grep后发现,它并没有实现这一点。因此,我可能需要一个更高级的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

    1 回复  |  直到 7 年前
        1
  •  12
  •   personal_cloud    7 年前

    BaseHTTPServer 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)