首頁  >  文章  >  運維  >  如何進行gunicorn Arbiter 原始碼解析

如何進行gunicorn Arbiter 原始碼解析

PHPz
PHPz轉載
2023-05-12 16:28:181381瀏覽

如前所述,Arbiter是gunicorn master進程的核心。 Arbiter主要負責管理worker進程,包括啟動、監控、殺死Worker進程;同時,Arbiter在某些訊號發生的時候還可以熱更新(reload)App應用,或是在線上升級gunicorn。 Arbiter的核心程式碼在一個檔案裡面,程式碼量也不大,原始碼在此:https://github.com/benoitc/gunicorn。

Arbiter主要有以下方法:

setup:

    處理設定項,最重要的是worker數量和worker工作模型

init_signal:

##    註冊訊號處理函數

handle_xxx:

    個別訊號特定的處理函數

kill_worker,kill_workers:

    向worker程式發出訊號

spawn_worker, spawn_workers:

#    fork出新的worker進程

murder_workers:

    殺死一段時間內未回應的worker進程

manage_workers:

#    根據檔案的worker數量,以及目前active的worker數量,決定是要fork或kill worker程序

reexec

    接收到訊號SIGUSR2調用,線上升級gunicorn

reload:

    接收到訊號SIGHUP調用,會根據新的配置新啟動worker進程,並殺死先前的worker進程

sleep

    在沒有訊號處理的時候,利用select的timeout進行sleep,可被喚醒

wakeup

    透過向管道寫訊息,喚醒進程

run

    主循環

  Arbiter真正被其他程式碼(Application)呼叫的函數只有__init__和run方法,在一句程式碼裡:

    Arbiter(self).run()

  上面程式碼中的self即為Application實例,其中_ _init__呼叫setup進行配置項設定。以下是run方法偽代碼


def run()
    self.init_signal()
    self.LISTENERS = create_sockets(self.cfg, self.log)
    self.manage_workers()    while True:        if no signal in SIG_QUEUE
            self.sleep()        else:
            handle_signal()


# 關於fork子程序

  fork子程序的程式碼在spawn_worker, 原始碼如下:

 Arbiter.spawn_worker如何进行gunicorn Arbiter 源码解析

  主要流程:

    (1)載入worker_class並實例化(預設為同步模型SyncWorker)

    (2)父進程(master進程)fork之後return,之後的邏輯都在子進程中運行

    (3)呼叫worker.init_process 進入循環,新航道雅思培訓的所有工作都在這個循環中

    (4)循環結束之後,呼叫sys.exit(0)

    (5)最後,在finally中,記錄worker進程的退出

#    

    下面是我自己寫的一點程式碼,簡化了主要的fork流程


 1 # prefork.py 2 import sys 3 import socket 4 import select 5 import os 6 import time 7   8 def do_sub_process(): 9     pid = os.fork()10     if pid < 0:11         print &#39;fork error&#39;12         sys.exit(-1)13     elif pid > 0:14         print 'fork sub process %d'  % pid15         return16  17     # must be child process18     time.sleep(1)19     print 'sub process will exit', os.getpid(), os.getppid()20     sys.exit(0)21  22 def main():23     sub_num = 224     for i in range(sub_num):25         do_sub_process()26     time.sleep(10)27     print 'main process will exit', os.getpid()28  29 if __name__ == '__main__':30     main()




在測試環境下輸出:  fork sub process 9601

  fork sub process 9602

  sub process will 9602 9600

  main process will exit 9600

  需要注意的是第20行呼叫了

sys.exit

, 保證子程序的結束,否則會繼續main函數中for迴圈,以及之後的邏輯。註解掉第19行重新運行,看輸出就懂了。
  • 關於kill子程序  master程序要kill worker程序就很簡單了,直接發訊號,原始碼如下:

  • #
     1     def kill_worker(self, pid, sig): 2         """\ 3         Kill a worker 4  5         :attr pid: int, worker pid 6         :attr sig: `signal.SIG*` value 7          """ 8         try: 9             os.kill(pid, sig)10         except OSError as e:11             if e.errno == errno.ESRCH:12                 try:13                     worker = self.WORKERS.pop(pid)14                     worker.tmp.close()15                     self.cfg.worker_exit(self, worker)16                     return17                 except (KeyError, OSError):18                     return19             raise

  • 關於sleep與wakeup

#  我們再來看看Arbiter的sleep和wakeup。 Arbiter在沒有訊號需要處理的時候會"sleep",當然,不是真正呼叫time.sleep,否則訊號來了也不能第一時間處理。這裡得實現比較巧妙,利用了管道和select的timeout。看程式碼就知道了

        def sleep(self):        """\
        Sleep until PIPE is readable or we timeout.
        A readable PIPE means a signal occurred.        """
            ready = select.select([self.PIPE[0]], [], [], 1.0) # self.PIPE = os.pipe()
            if not ready[0]: 
                return
            while os.read(self.PIPE[0], 1):                pass

  程式碼裡面的註解寫得非常清楚,要嘛PIPE可讀立即返回,要嘛等待逾時。管道可讀是因為有訊號發生。這裡看看pipe函數

############  os.######pipe###()############Create a pipe. Return a pair of file descriptors ###(r,w)### usable for reading and writing, respectively.############  那我們看一下什麼時候管道可讀:一定是往管道寫入的東西,這就是wakeup函數的功能###
        def wakeup(self):            """
            Wake up the arbiter by writing to the PIPE            """
            os.write(self.PIPE[1], b'.')
######最後附上Arbiter的訊號處理###:######退出,INT:快速關閉###### #TERM: 優雅關機。等待工作人員完成其當前請求,直到逾時。 ###

HUP:重新載入配置,用新配置啟動新的工作進程,並優雅地關閉舊的工作進程。如果應用程式未預先載入(使用--preload選項),Gunicorn也會載入新版本。

TTIN:將進程數增加一個

TTOU:將進程數減少一個

USR1:重新開啟日誌檔案

USR2:在飛行中升級Gunicorn。應使用單獨的術語訊號終止舊進程。此訊號也可用於使用預先載入應用程式的新版本。

絞盤:當Gunicorn被守護時,優雅地關閉工作流程。

以上是如何進行gunicorn Arbiter 原始碼解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除