一,獨角獸
Gunicorn 服務器作為wsgi app的容器, 采用 pre-fork 模型中有一個管理進程以及幾個的工作進程。master 管理多個 slave 進程
創建slave進程,監聽事件:
1, 根據定義的 work數量 創建多個 work 進程
2, 在worker.init_process()函數中,每個woker子進程都會單獨去實例化我們的wsgi app對象。針對多進程,一個進程實例化一個 app 對象,多線程,一個線程處理實例化app對象,協程根據server對象
3, 然后,worker和master各自進入自己的消息循環。 master的事件循環就是收收信號,管理管理worker進程,
而worker進程的事件循環就是監聽網絡事件並處理(如新建連接,斷開連接,處理請求發送響應等等),真正的連接最終是連到了worker進程上的

# 創建 固定數量的 worker(由 manager 進行維護) def manage_workers(self): if len(self.WORKERS.keys()) < self.num_workers: self.spawn_workers() while len(workers) > self.num_workers: (pid, _) = workers.pop(0) self.kill_worker(pid, signal.SIGQUIT) # 創建 work 進程 def spawn_worker(self): self.worker_age += 1 #創建worker。請注意這里的app 對象並不是真正的wsgi app對象,而是gunicorn的app #對象。gunicorn的app對象負責import我們自己寫的wsgi app對象。 worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS, self.app, self.timeout / 2.0, self.cfg, self.log) # pid = 0 表示子進程 pid = os.fork() #父進程,返回后繼續創建其他worker,沒worker后進入到自己的消息循環 if pid != 0: # 父進程記錄子進程 self.WORKERS[pid] = worker return pid # 子進程繼續運行 # Process Child worker_pid = os.getpid() try: .......... worker.init_process() #子進程,初始化woker,進入worker的消息循環, sys.exit(0) except SystemExit: raise ............
二, Gunicorn 幾種啟動方式 sync (默認值) eventlet gevent tornado
IO 受限 -建議使用gevent或者asyncio
CPU受限 -建議增加workers數量
不確定內存占用? -建議使用gthread
# gunicorn 啟動4個進程(默認啟動方式),每個 work 單線程 並發量 4*1 gunicorn -w 4 -b 0.0.0.0:8000 demo:app # gunicorn 允許每個worker擁有多個線程 並發量 = 4*2 gunicorn -w 4 --thread=2 --worker-class=gthread main:app # gunicorn 偽線程 gevent (協程) 並發量 3*1000 gunicorn --worker-class=gevent --worker-connections=1000 -w 3 main:app
三, web 異步任務實現方式:
1, web 的多並發 並不是有 flask 來實現的, flask 只是負責根據Request產生Response(一個python程序),
多並發是由 WSGI server是通過進程(pre-fork)來並發的。這樣並發就取決與進程數,如果WSGI server用了gevent,
eventlet等 green thread技術,就可以支持更多並發
2, 針對 work 啟動方式 gevent
每個ggevent worker啟動的時候會啟動多個server對象,worker首先為每個listener創建一個server對象,
每個server對象都有運行在一個單獨的gevent pool對象中。真正等待鏈接和處理鏈接的操作是在server對象中進行的
3,WSGIServer 實際上是創建一個協程去處理該套接字,也就是說在WSGIServer 中,一個協程單獨負責一個HTTP鏈接。
協程中運行的self._handle函數實際上是調用了WSGIHandler的handle函數來不斷處理http 請求
4,gunicorn 會啟動一組 worker進程,所有worker進程公用一組listener,在每個worker中為每個listener建立一個wsgi server。
每當有HTTP鏈接到來時,wsgi server創建一個協程來處理該鏈接,協程處理該鏈接的時候,先初始化WSGI環境,
然后調用用戶提供的app對象去處理HTTP請求(處理http請求可以理解為一個程序)。

#為每個listener創建server對象。 for s in self.sockets: pool = Pool(self.worker_connections) #創建gevent pool if self.server_class is not None: #創建server對象 server = self.server_class( s, application=self.wsgi, spawn=pool, log=self.log, handler_class=self.wsgi_handler, **ssl_args) ............. server.start() #啟動server,開始等待鏈接,服務鏈接 servers.append(server) .........
在handle函數的循環內部,handle_one_request函數首先讀取HTTP 請求,初始化WSGI環境,然后最終調用run_application函數來處理請求
def run_application(self): self.result = self.application(self.environ, self.start_response) self.process_result()