我正在寻找一个标准的Python 2.7软件包,该软件包提供一个HTTP服务器,该服务器在同一端口号上同时进行流传输连接。

嗨,主持人,请不要再将我的问题标记为希望以非流式方式提供的问题的重复形式,例如 Multithreaded web server in python。不,我不希望像ThreadingMixIn这样的hack仅仅收集响应并将其作为一个单元返回。

换句话说,我正在寻找执行以下示例程序所要执行的标准方法-但无需自己编写整个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似乎正在尝试保持事件,这将需要使用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或某些此类包可靠地进行流传输?

最佳答案

默认的BaseHTTPServer设置将在每个监听器上重新绑定(bind)一个新的套接字,如果所有监听器都在同一端口上,则在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)

10-08 17:39