gunicorn 工做原理

##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請求。

相關文章
相關標籤/搜索