Gunicorn源码分析|牛气冲天新年征文

985 阅读2分钟

由gunicorn执行程序入口说起, gunicorn version:20.x

def app(environ, start_response):
        data = b"Hello, World!\n"
        start_response("200 OK", [
            ("Content-Type", "text/plain"),
            ("Content-Length", str(len(data)))
        ])

        return iter([data])

1. gunicorn入口点

使用gunicorn作为服务器执行程序时,执行命令为:gunicorn -w 2 gunicorn_app:app 对应到gunicorn中setup.py入口点

[console_scripts]
    gunicorn=gunicorn.app.wsgiapp:run

实际执行的是gunicorn.app.wsgiapp:run -w 2 gunicorn_app:app

2. 执行流程

2.1. 准备参数配置阶段

def run():
    """\
    The ``gunicorn`` command line runner for launching Gunicorn with
    generic WSGI applications.
    """
    from gunicorn.app.wsgiapp import WSGIApplication
    WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()

WSGIApplication(init) ——> Application(run)——> BaseApplication(init, run) 首先执行Base类的初始化方法,

 def __init__(self, usage=None, prog=None):
        self.usage = usage
        self.cfg = None
        self.callable = None
        self.prog = prog
        self.logger = None
        self.do_load_config()

def do_load_config(self):
        """
        Loads the configuration
        """
        try:
            self.load_default_config()
            self.load_config() #执行的是子类wsgi Application的覆盖方法
            """
            1. parse() 使用argparse完成命令行参数生成得到匹配对象。parser.parse_args()进行参数解析       
            2. 执行wsgi类的init方法,设置app应用,app_uri='gunicorn_app:app'
            3.chdir 工作目录
            4. 加载配置:1. 通过-c指定的文件;或者系统环境下的GUNICORN_CMD_ARGS 变量;或者当前目录下的gunicorn.conf.py文件 2. 命令行参数。有文件,则加载文件,没有则加载输入的命令行参数。当同时配置了文件和命令参数时,配置会被命令参数覆盖。
           """
        except Exception as e:
            print("\nError: %s" % str(e), file=sys.stderr)
            sys.stderr.flush()
            sys.exit(1)

def load_default_config(self):
        # init configuration 
        self.cfg = Config(self.usage, prog=self.prog) #装载配置项settings,使用到了元类

def run(self):
        try:
            Arbiter(self).run()
        except RuntimeError as e:
            print("\nError: %s\n" % e, file=sys.stderr)
            sys.stderr.flush()
            sys.exit(1)

2.2. 执行run

def run(self):
        if self.cfg.print_config:
            print(self.cfg) #是否打印配置信息,执行的是config类的`__str__`方法

        if self.cfg.print_config or self.cfg.check_config:
            try:
                self.load()
            except Exception:
                msg = "\nError while loading the application:\n"
                print(msg, file=sys.stderr)
                traceback.print_exc()
                sys.stderr.flush()
                sys.exit(1)
            sys.exit(0)

        if self.cfg.spew:
            debug.spew()

        if self.cfg.daemon:
            util.daemonize(self.cfg.enable_stdio_inheritance)

        # set python paths
        if self.cfg.pythonpath:
            paths = self.cfg.pythonpath.split(",")
            for path in paths:
                pythonpath = os.path.abspath(path)
                if pythonpath not in sys.path:
                    sys.path.insert(0, pythonpath)
       
        #前面条件不满足,直接执行父类的方法
        super().run() # Arbiter的run方法

Arbiter类主要做几件事:

  1. Arbiter.__init__
   self.setup(app) #app即gunicorn.app.Application实例
   1.将一些配置数据赋值给Arbiter属性
   2.设置env环境变量,参数raw_env
  self.master_name = "Master"  
  1. Arbiter.run()
self.init_signals()  #Initialize master signal handling. Most of the signals are queued. Child signals only wake up the master
self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds)  # 创建socket 并进行监听,最多重试5次创建socket
self.manage_workers()  ,完成的事情
1.执行worker数量值个self.spawn_workers(),即fork子进程spawn_worker;spawn_worker做了以下事:
  I. 加载worker_class并实例化处理,即不同的worker类
  II.父进程fork子进程之后return,子进程处理逻辑
  III. 执行worker.init_process(),worker开始工作,交给支持的worker开始处理请求
  IV.worker完成后, sys.exit(0)退出。   
  1. workers
    sync、eventlet、gevent、gevent_wsgi、gevent_pywsgi、tornado、gthread
  2. init 初始化父进程pid、address、socket、cfg
  3. init_process 函数
init_signals() 信号初始化
load_wsgi()   调用应用程序
run() #由子类实现,即具体的worker, 接收请求并由handle_request处理请求,流程和werkzeug一样。

3. 总结

由gunicorn创建一个master主进程,然后主进程创建子进程,并传入父进程套接字。当进程alive的时候,会不断进行循环进行accept请求,交由wsgi应用处理。