引言
前段時間分享了一篇如何提高django的并發能力的文章,由此引發了我對gunicorn的進一步研究,利用業余的時間看了一下gunicorn關于worker那部分的主要代碼,下邊以gevent為例子聊一聊它的工作流程(好吧目前主要看了看gevent這塊兒的代碼)。
歸納流程
- gunicorn采用了n+1個進程,n就是真勞動worker數,即通過-w指定。1則是主worker,它負責管理這些勞動worker,它主要的工作就是對勞動worker進行增與殺,另外他會監聽一個端口,外提供服務。
- 主worker在監聽端口會注冊一個文件描述符fd。
- 勞動worker通過注冊的文件描述符不斷的接受請求。
部分細節分析
說明
如果對部分細節有興趣的話可以繼續看一下,當然默認你已經找到了gunicorn的代碼位置,一般在Python(安裝路徑)/lib/python3.x/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__.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方法啟動時會導致數據庫連接不能復用,后邊會繼續研究,希望能找到具體的原因。