前述したように、Arbiter はガニコーン マスター プロセスの中核です。 Arbiter は主に、ワーカー プロセスの起動、監視、強制終了などのワーカー プロセスの管理を担当しますが、同時に、特定の信号が発生したときにアプリ アプリケーションのホット アップデート (リロード) やオンラインでの gunicorn のアップグレードも行うことができます。 Arbiter のコア コードは 1 つのファイルに含まれており、コード量はそれほど多くありません。ソース コードはここにあります: https://github.com/benoitc/gunicorn。
Arbiter には主に次のメソッドがあります:
setup:
設定項目の処理。最も重要なものはワーカーの数と作業中のワーカーです。 model
init_signal:
信号処理関数の登録
handle_xxx:
それぞれの固有の処理関数signal
kill_worker、kill_workers:
ワーカー プロセスにシグナルを送信します
spawn_worker、spawn_workers:
フォークして新しいワーカー プロセスを作成します
murder_workers:
一定期間応答しないワーカー プロセスを強制終了します
manage_workers:
構成ファイル内のワーカーの数と現在アクティブなワーカーの数に基づいて、ワーカー プロセスをフォークするか強制終了するかを決定します
reexec:
シグナル SIGUSR2 呼び出しを受信した後、gunicorn をオンラインでアップグレードします
#reload:
シグナル SIGHUP 呼び出しを受信した後、ワーカー プロセスは新しい構成に基づいて開始され、以前のワーカー プロセスは強制終了されます
sleep:
信号処理がない場合は、選択タイムアウトを使用してスリープし、ウェイクアップ
#wakeup:
パイプにメッセージを書き込んでプロセスをウェイクアップ
run:
メイン ループ
他のコード (アプリケーション) __init__ によって実際に呼び出されるアービターの唯一の関数とメソッドを 1 行のコードで記述します:
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) 親プロセス (マスタープロセス) は fork 後に戻り、後続のロジックはすべて子プロセスで実行されます (3) worker.init_process を呼び出しますループに入り、すべての新しいチャネル IELTS トレーニング作業はすべてこのループ内にあります (4) ループが終了したら、sys.exit(0) (5) 最後に、最後に、ワーカー プロセスの終了を記録します 以下は私が自分で書いたコードの一部で、メインのフォーク プロセスを簡素化します#
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 exit 9601 9600
サブプロセスは終了します 9602 9600
メインプロセスは終了します 9600
20 行目で
sys.exitを呼び出して、子プロセスが確実に終了するようにし、それ以外の場合はメイン関数の for ループが続行され、その後のロジックが続行されます。 19行目をコメントアウトして再度実行すると、出力を見れば分かります。
子プロセスの強制終了についてマスター プロセスがワーカー プロセスを強制終了するのは非常に簡単です。シグナルを直接送信します。ソース コードは次のとおりです:
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
Arbiter の睡眠と覚醒について見てみましょう。 Arbiter は、処理する信号がないときに「スリープ」します。もちろん、実際には time.sleep を呼び出しません。そうしないと、信号が届いてもすぐに処理されません。ここでの実装は、パイプと選択タイムアウトを使用して、より賢明です。コードを見るだけで、
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()
def wakeup(self): """ Wake up the arbiter by writing to the PIPE """ os.write(self.PIPE[1], b'.')
最後にアービターの信号処理が取り付けられます
:Exit, INT: 迅速なシャットダウン
TERM: 正常なシャットダウン。タイムアウトになるまで、ワーカーが現在のリクエストを完了するのを待ちます。HUP: 構成をリロードし、新しい構成で新しいワーカー プロセスを開始し、古いワーカー プロセスを正常にシャットダウンします。アプリケーションがプリロードされていない場合 (--preload オプションを使用)、Gunicorn は新しいバージョンもロードします。
TTIN: プロセス数を 1 つ増やします
TTOU: プロセス数を 1 つ減らします
USR1: ログ ファイルを再度開きます
USR2: Gunicon をその場でアップグレードします。古いプロセスを終了するには、別の用語シグナルを使用する必要があります。この信号は、プリロードされた新しいバージョンのアプリケーションを使用するためにも使用できます。
Winch: Gunicorn がデーモン化されたときにワーカー プロセスを正常にシャットダウンします。
以上がGunicorn Arbiter のソースコードを分析する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。