>  기사  >  운영 및 유지보수  >  Gunicorn Arbiter 소스 코드를 분석하는 방법

Gunicorn Arbiter 소스 코드를 분석하는 방법

PHPz
PHPz앞으로
2023-05-12 16:28:181378검색

앞서 언급했듯이 Arbiter는 Gunicorn 마스터 프로세스의 핵심입니다. Arbiter는 작업자 프로세스 시작, 모니터링 및 종료를 포함하여 작업자 프로세스를 관리하는 일을 주로 담당합니다. 동시에 Arbiter는 특정 신호가 발생할 때 앱 애플리케이션을 핫 업데이트(다시 로드)하거나 온라인으로 gunicorn을 업그레이드할 수도 있습니다. Arbiter의 핵심 코드는 하나의 파일에 들어 있으며, 코드 양은 많지 않습니다. 소스 코드는 https://github.com/benoitc/gunicorn입니다.

Arbiter에는 주로 다음과 같은 방법이 있습니다.

setup:

구성 항목을 처리하는데, 가장 중요한 것은 작업자 수와 작업자 작업 모델입니다.

init_signal:

신호 처리 기능 등록

handle_xxx :

각 신호의 특정 처리 기능

kill_worker, kill_workers:

작업자 프로세스에 신호 보내기

spawn_worker,spawn_workers:

새 작업자 프로세스를 포크합니다

murder_workers:

일정 시간 내에 응답하지 않는 작업자 프로세스 종료

manage_workers:

구성 파일의 작업자 수와 현재 활성 작업자 수를 기반으로 작업자 프로세스를 분기할지 종료할지 결정합니다.

reexec :

신호 SIGUSR2 호출 수신, 온라인 업그레이드 gunicorn

reload:

신호 SIGHUP 호출을 받은 후 작업자 프로세스는 새로운 구성에 따라 시작되고 이전 작업자 프로세스는 종료됩니다

sleep :

신호 처리가 없는 경우 선택 타임아웃을 사용하여 잠자기하고 깨울 수 있습니다.

wakeup:

파이프에 메시지를 작성하여 프로세스를 깨우고

run:

메인 루프

실제로 다른 코드(애플리케이션)에 의해 Arbiter가 호출되는 유일한 함수는 __init_ _이며 코드 한 문장에서 실행 메서드는 다음과 같습니다.

Arbiter(self).run()

  위 코드의 self는 애플리케이션 인스턴스입니다. , 여기서 __init__는 setup을 호출하여 구성 항목을 설정합니다. 다음은 실행 메소드의 의사 코드입니다


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()


포크 하위 프로세스에 대해

포크 하위 프로세스에 대한 코드는spawn_worker에 있으며, 소스 코드는 다음과 같습니다.

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

기본 프로세스:

(1) Worker_class 로드 및 인스턴스화(기본값은 동기 모델 SyncWorker)

(2) 상위 프로세스(마스터 프로세스)는 포크 후 반환되고 모든 후속 로직은 하위에서 실행됩니다. process

(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 &#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 프로세스 9601

 fork 하위 프로세스 9602

 하위 프로세스는 9601 9 600

하위 프로세스는 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


잠자기 및 wakeup

Arbiter sleep과 wakeup을 살펴보겠습니다. Arbiter는 처리할 신호가 없을 때 "sleep"합니다. 물론 실제로 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는 읽을 수 있고 즉시 반환되거나 대기 시간이 초과됩니다. 신호가 발생하기 때문에 파이프를 읽을 수 있습니다. 파이프 함수를 살펴보세요

  •  os.pipe()  os.pipe()

  • Create a pipe. Return a pair of file descriptors (r,w)

파일 설명자 쌍을 반환합니다. (r, w) 는 각각 읽기 및 쓰기에 사용할 수 있습니다.

그런 다음 파이프를 읽을 수 있는 경우를 살펴보겠습니다. 파이프에 기록된 내용이어야 합니다. 이것은 wakeup 함수

        def wakeup(self):            """
            Wake up the arbiter by writing to the PIPE            """
            os.write(self.PIPE[1], b'.')
입니다.

마지막으로 Arbiter 신호 처리를 연결합니다.

:

Exit, INT: 빠른 종료 🎜🎜TERM: 우아한 종료. 시간이 초과될 때까지 작업자가 현재 요청을 완료할 때까지 기다립니다. 🎜

HUP: 구성을 다시 로드하고, 새로운 구성으로 새 작업자 프로세스를 시작하고, 이전 작업자 프로세스를 정상적으로 종료합니다. Gunicorn은 애플리케이션이 사전 로드되지 않은 경우에도 새 버전을 로드합니다(--preload 옵션 사용).

TTIN: 프로세스 수를 하나씩 늘립니다.

TTOU: 프로세스 수를 하나씩 줄입니다.

USR1: 로그 파일을 다시 엽니다.

USR2: 즉시 Gunicorn을 업그레이드합니다. 이전 프로세스를 종료하려면 별도의 용어 신호를 사용해야 합니다. 이 신호는 사전 로드된 새 버전의 애플리케이션을 사용하는 데에도 사용될 수 있습니다.

Winch: Gunicorn이 데몬화되면 작업자 프로세스를 정상적으로 종료합니다.

위 내용은 Gunicorn Arbiter 소스 코드를 분석하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제