https://futures.yunsonbai.top/?hmsr=yunsonbai.top
贵金属行情

gunicorn的worker调度

https://futures.yunsonbai.top/?hmsr=yunsonbai.top

引言

前段时间分享了一篇如何提高django的并发能力的文章,由此引发了我对gunicorn的进一步研究,利用业余的时间看了一下gunicorn关于worker那部分的主要代码,下边以gevent为例子聊一聊它的工作流程(好吧目前主要看了看gevent这块儿的代码)。

归纳流程

  • gunicorn采用了n+1个进程,n就是真劳动worker数,即通过-w指定。1则是主worker,它负责管理这些劳动worker,它主要的工作就是对劳动worker进行增与杀,另外他会监听一个端口,外提供服务。
  • 主worker在监听端口会注册一个文件描述符fd。
  • 劳动worker通过注册的文件描述符不断的接受请求。

部分细节分析

说明

如果对部分细节有兴趣的话可以继续看一下,当然默认你已经找到了gunicorn的代码位置,一般在Python(安装路径)/lib/python3.5/site-packages/gunicorn

worker的创建

  • 启动最先调用app/wsgiapp.py的run函数
  • 通过WSGIApplication(“%(prog)s [OPTIONS] [APP_MODULE]”).run()调用后边的核心代码
  • 根据执行的函数追踪,最终会调用到arbiter.py的run函数,这才开始了漫长的创建过程,看一下核心代码(篇幅原因,部分粘贴)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def run(self):
    "Main master loop."
    # 这里启动主进程,并监听端口,注册文件描述符
    self.start()
    util._setproctitle("master [%s]" % self.proc_name)
    try:
    # 这里负责启动所有的从worker,也就是劳动worker
    self.manage_workers()
    while True:
    self.maybe_promote_master()
    sig = self.SIG_QUEUE.pop(0) if self.SIG_QUEUE else None
    if sig is None:
    self.sleep()
    # 则是kill掉那些unused/idle workers
    self.murder_workers()
    self.manage_workers()

    # 补充一下,所有的劳动worker,都放在self.WORKERS里,
    # 包含每个worker的pid等信息,主worker根据劳动者的运行时间等来决定是否将其杀死,
    # 可以看一下murder_workers函数的实现

劳动者与文件描述符绑定

  • 上边提到的self.manage_workers()先会调用spawn_worker函数,部分代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    worker.pid = os.getpid()
    try:
    util._setproctitle("worker [%s]" % self.proc_name)
    self.log.info("Booting worker with pid: %s", worker.pid)
    self.cfg.post_fork(self, worker)
    # 通过这里来调用你指定的worker的run函数,这里我指定的是gevent
    worker.init_process()
    sys.exit(0)
    except SystemExit:
    raise
    # 关于有哪几种worker可以被调用,可以查看workers/__init__.py
  • ggevent.py的run函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 为什么看ggevent,可以看一下works/__init__.py文件
    if self.server_class is not None:
    environ = base_environ(self.cfg)
    environ.update({
    "wsgi.multithread": True,
    "SERVER_SOFTWARE": VERSION,
    })
    server = self.server_class(
    s, application=self.wsgi, spawn=pool, log=self.log,
    handler_class=self.wsgi_handler, environ=environ,
    **ssl_args)
    else:
    # s其实就是监听端口得到的那个socket
    hfun = partial(self.handle, s)
    # 这里就是绑定文件描述符的关键
    server = StreamServer(s, handle=hfun, spawn=pool, **ssl_args)

    server.start()
    servers.append(server)

    # StreamServer是gevent提供的一个类,继承了BaseServer
    # 在BaseServer完成处理方法的绑定(If given, the request handler)

总结

虽然没有看其他的工作方式流程,但大概应该差不多都是最前的三步过程,可能会有一些函数的不同,其实去看gunicorn的源码无非就是想了解其工作流程,前边的文章如何提高django的并发能力,遇到了当采用gevent方法启动时会导致数据库连接不能复用,后边会继续研究,希望能找到具体的原因。

文章推荐

gunicorn+gevent+django解决mysql高连接数并提高性能

yunsonbai wechat
公众号:技术and生活