引言
前段时间分享了一篇如何提高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
20def 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
11worker.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__.pyggevent.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方法启动时会导致数据库连接不能复用,后边会继续研究,希望能找到具体的原因。