如前所述,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調用,線上升級gunicornreload:
接收到訊號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
主要流程: (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 'fork error'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
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
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中文網其他相關文章!