gunicorn学习总结

简介

Gunicorn是一个Python WSGI HTTP Server。WSGI代表Web服务器网关接口(Python Web Server Gateway Interface),是为Python语言定义的Web服务器Web应用程序或框架之间的一种简单而通用的接口。主要负责:接受HTTP请求、解析HTTP请求、发送HTTP响应。

简单使用:

1
2
# gunicorn [OPTIONS] APP_MODULE
gunicorn -w 4 myapp:app
  • APP_MODULE形如:$(MODULE_NAME):$(VARIABLE_NAME)
  • MODULE_NAME是app模块源代码.py文件的路径(/.代替)。
  • VARIABLE_NAME指的是WSGI callable的一个变量名称,也可以是一个返回WSGI callable的函数调用:$ gunicorn "myproject:create_app()"

日志

Gunicorn主要的logger:

  • gunicorn.error
  • gunicorn.access
  • gunicorn.http

可以将Gunicorn的logger对应的handler把原始模块的logging的handler替换(或者添加),这样原始logger输出的日志也会被Gunicorn的logger对应的handler所处理。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
logger_name = 'gunicorn.error'
gunicorn_err_logger = logging.getLogger(logger_name)
date_log_file_name = "logs/dialogue-system.log"
handler = TimedRotatingFileHandler(date_log_file_name, when="midnight", interval=1)
handler.suffix = "%Y%m%d"
gunicorn_err_logger.addHandler(handler)
logging.root.handlers = gunicorn_err_logger.handlers # 这里直接替换handler
logging.root.level = gunicorn_err_logger.level
formatter = logging.Formatter("%(asctime)-15s %(levelname)s %(filename)s ln: %(lineno)d "
"pid: %(process)d msg: %(message)s")
for handler in logging.root.handlers:
handler.setFormatter(formatter)
logging.info(f"logging handlers: {logging.root.handlers}, logging level: {logging.root.level}")

信号

所有信号的定义:官网文档

常用:

重启app:一个个关闭worker,然后再一个个开启worker(并不是关一个,起一个,再关下一个)。

1
kill -HUP masterpid

架构

Gunicorn是一种基于pre-fork的woker模型。这里的pre是指在请求到来之前,所有的worker已经fork好了。也就是说,master是一个进程,所有的woker是都有单独的进程。

master

master进程是一个循环监听器,接收各种信号。

sync worker

默认的woker类型就是同步woker,一个worker在同一时刻只能处理一个请求。同步woker不支持持久连接。

async worker

异步woker基于greenlets(通过eventlet或者gevent)。

调用

用gunicorn启动一个app时的调用顺序:

1
2
3
4
5
6
7
bin.gunicorn -> gunicorn.app.wsgiapp:WSGIApplication.run()
->gunicorn.arbiter:Arbiter.run()
-> .start() # 启动gunicorn ; manage_workers() # 启动worker
-> spawn_workers() -> spawn_worker()
-> 某一个woker(如默认gunicorn.wokers.sync:SyncWorker)的init_process() -> .load_wsgi()
-> gunicorn.app.wsgiapp:WSGIApplication.load()
-> gunicorn.utils:import_app() # 开始加载用户的app代码

utils:import_app():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def import_app(module):
parts = module.split(":", 1)
if len(parts) == 1:
module, obj = module, "application"
else:
module, obj = parts[0], parts[1]

try:
__import__(module)
except ImportError:
if module.endswith(".py") and os.path.exists(module):
msg = "Failed to find application, did you mean '%s:%s'?"
raise ImportError(msg % (module.rsplit(".", 1)[0], obj))
else:
raise

mod = sys.modules[module]

is_debug = logging.root.level == logging.DEBUG
try:
app = eval(obj, vars(mod))

也就是:gunicorn的worker使用__import__加载用户的app module代码,再使用eval()获取app对象。

使用案例

gunicorn的文档相对比较少,建议多看官方仓库的example目录