##gunicorn工做原理django
Gunicorn「綠色獨角獸」是一個被普遍使用的高性能的Python WSGI UNIX HTTP服務器,移植自Ruby的獨角獸(Unicorn )項目,使用pre-fork worker模式,具備使用很是簡單,輕量級的資源消耗,以及高性能等特色。flask
Gunicorn 服務器做爲wsgi app的容器,可以與各類Web框架兼容(flask,django等),得益於gevent等技術,使用Gunicorn可以在基本不改變wsgi app代碼的前提下,大幅度提升wsgi app的性能。服務器
###整體結構網絡
gunicorn pre-fork worker模型中有一個管理進程以及幾個的工做進程。管理進程:master,工做進程:worker。(如下代碼中爲了方面理解,均去除了一些干擾代碼)app
master經過pre-fork的方式建立多個worker:框架
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 = os.fork() if pid != 0: #父進程,返回後繼續建立其餘worker,沒worker後進入到本身的消息循環 self.WORKERS[pid] = worker return pid # Process Child worker_pid = os.getpid() try: .......... worker.init_process() #子進程,初始化woker,進入worker的消息循環, sys.exit(0) except SystemExit: raise ............
在worker.init_process()函數中,worker中gunicorn的app對象會去import 咱們的wsgi app。也就是說,每一個woker子進程都會單獨去實例化咱們的wsgi app對象。每一個worker中的swgi app對象是相互獨立、互不干擾的。socket
manager維護數量固定的worker:函數
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)
建立完全部的worker後,worker和master各自進入本身的消息循環。 master的事件循環就是收收信號,管理管理worker進程,而worker進程的事件循環就是監聽網絡事件並處理(如新建鏈接,斷開鏈接,處理請求發送響應等等),因此真正的鏈接最終是連到了worker進程上的。(注:有關這種多進程模型的詳細介紹,能夠參考http://blog.csdn.net/largetalk/article/details/7939080)tornado
###worker性能
woker有不少種,包括:ggevent、geventlet、gtornado等等。這裏主要分析ggevent。
每一個ggevent worker啓動的時候會啓動多個server對象:worker首先爲每一個listener建立一個server對象(注:爲何是一組listener,由於gunicorn能夠綁定一組地址,每一個地址對於一個listener),每一個server對象都有運行在一個單獨的gevent pool對象中。真正等待連接和處理連接的操做是在server對象中進行的。
#爲每一個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) .........
上面代碼中的server_class其實是一個gevent的WSGI SERVER的子類:
class PyWSGIServer(pywsgi.WSGIServer): base_env = BASE_WSGI_ENV
須要注意的是構造PyWSGIServer的參數:
self.server_class( s, application=self.wsgi, spawn=pool, log=self.log, handler_class=self.wsgi_handler, **ssl_args)
這些參數中s是server用來監聽連接的套接字。spawn是gevent的協程池。application便是咱們的wsgi app(通俗點講就是你用 flask 或者 django寫成的app),咱們的app就是經過這種方式交給gunicorn的woker去跑的。 handler_class是gevent的pywsgi.WSGIHandler子類。
當全部server對象建立完畢後,worker須要定時通知manager,不然會被認爲是掛掉了。
while self.alive: self.notify() .......
這個地方的notify機制設計的比較有趣,每一個worker有個與之對應的tmp file,每次notify的時候去操做一下這個tmp file(好比經過os.fchmod),這個tmp file的last update的時間戳就會更新。而manager則經過檢查每一個worker對應的temp file的last update的時間戳,來判斷這個進程是不是掛掉的。
###WSGI SERVER
真正等待連接和處理連接的操做是在gevent的WSGIServer 和 WSGIHandler中進行的。 最後再來看一下gevent的WSGIServer 和 WSGIHandler的主要實現:
WSGIServer 的start函數裏面調用start_accepting來處理到來的連接。在start_accepting裏面獲得接收到的套接字後調用do_handle來處理套接字:
def do_handle(self, *args): spawn = self._spawn spawn(self._handle, *args)
能夠看出,WSGIServer 其實是建立一個協程去處理該套接字,也就是說在WSGIServer 中,一個協程單獨負責一個HTTP連接。協程中運行的self._handle函數其實是調用了WSGIHandler的handle函數來不斷處理http 請求:
def handle(self): try: while self.socket is not None: result = self.handle_one_request()#處理HTTP請求 if result is None: break if result is True: continue self.status, response_body = result self.socket.sendall(response_body)#發送迴應報文 ..............
在handle函數的循環內部,handle_one_request函數首先讀取HTTP 請求,初始化WSGI環境,而後最終調用run_application函數來處理請求:
def run_application(self): self.result = self.application(self.environ, self.start_response) self.process_result()
在這個地方纔真正的調用了咱們的 app。
總結:gunicorn 會啓動一組 worker進程,全部worker進程公用一組listener,在每一個worker中爲每一個listener創建一個wsgi server。每當有HTTP連接到來時,wsgi server建立一個協程來處理該連接,協程處理該連接的時候,先初始化WSGI環境,而後調用用戶提供的app對象去處理HTTP請求。