2017-09-14 40 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,...)'添加) –

相關問題