概述

如果你用过 Python 开发 Web 应用,例如利用 Flask 框架开发过 Web 应用的话,那么,你在启动 Python Web 服务时,一定看到过如下提示:

  1. * Serving Flask app "flask_server" (lazy loading)
  2. * Environment: production
  3. WARNING: This is a development server. Do not use it in a production deployment.
  4. Use a production WSGI server instead.
  5. * Debug mode: off

看到 WARNING 信息了吧~
提示这是一个开发服务器,不要直接用于生产环境部署,在生产环境下应该使用 WSGI 服务器。
那么,什么是 WSGI 服务器呢?
本文要介绍的 Gunicorn 就是其中的佼佼者。

WSGI 服务器

Web服务器网关接口Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器Web应用程序框架之间的一种简单而通用的接口)。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口。
WSGI 区分了两个部分:

  • 服务器/网关
  • 应用程序/框架

在处理一个WSGI请求时,服务器会为应用程序提供环境信息及一个回调函数(Callback Function)。当应用程序完成处理请求后,透过前述的回调函数,将结果回传给服务器。

所谓的“WSGI 中间件”同时实现了API的两方,因此可以在WSGI服务器和WSGI应用之间起调解作用:从Web服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。“中间件”组件可以执行以下功能:

  • 重写环境变量后,根据目标URL,将请求消息路由到不同的应用对象。
  • 允许在一个进程中同时运行多个应用程序或应用框架。
  • 负载均衡和远程处理,通过在网络上转发请求和响应消息。
  • 进行内容后处理,例如应用XSLT样式表。

gunicorn 介绍

Gunicorn 是一个用于 UNIX 的 Python WSGI HTTP 服务器,这是一个 pre-fork worker 模型。
Gunicorn 服务器与各种 Web 框架广泛兼容,实现简单,服务器资源消耗少,速度相当快。

gunicorn 实战

安装

开始 gunicorn 学习的第一步,我们还是来安装 gunicorn 。和大部分的 Python 库一样,gunicorn 的安装非常简单,只需要使用 pip 安装即可:

  1. pip install gunicorn

QuickStart

下面,我们来用一个最简示例演示一下 gunicorn 的使用:
编写一个最简单的应用程序 myapp.py 文件:

  1. def app(environ, start_response):
  2. data = b"Hello, World!\n"
  3. start_response("200 OK", [
  4. ("Content-Type", "text/plain"),
  5. ("Content-Length", str(len(data)))
  6. ])
  7. return iter([data])

可以看到,其中定义了一个 app 的函数。
下面,我们可以直接使用 gunicorn 来启动对应的 WEB 服务了:

  1. gunicorn -w 4 myapp:app
  2. # [2021-09-15 11:51:01 +0800] [10495] [INFO] Starting gunicorn 20.1.0
  3. # [2021-09-15 11:51:01 +0800] [10495] [INFO] Listening at: http://127.0.0.1:8000 (10495)
  4. # [2021-09-15 11:51:01 +0800] [10495] [INFO] Using worker: sync
  5. # [2021-09-15 11:51:01 +0800] [10497] [INFO] Booting worker with pid: 10497
  6. # [2021-09-15 11:51:01 +0800] [10498] [INFO] Booting worker with pid: 10498
  7. # [2021-09-15 11:51:01 +0800] [10499] [INFO] Booting worker with pid: 10499
  8. # [2021-09-15 11:51:01 +0800] [10500] [INFO] Booting worker with pid: 10500

此时,我们可以打开浏览器,访问 http://127.0.0.1:8000 来看一下:
image.png
好了,Web 应用服务器已经可以正常运行了。

Flask 应用集成

首先,我们准备一个简单的 Flask 应用程序代码:flask_demo.py

  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route("/")
  4. def hello_world():
  5. return "<p>Hello, World!</p>"
  6. if __name__ == '__main__':
  7. """
  8. # 主程序
  9. """
  10. app.run(
  11. host="0.0.0.0", port=8080
  12. )

此时,我们可以直接使用 gunicorn 来启动应用:

  1. gunicorn -w 4 -b 127.0.0.1:4000 flask_demo:app
  2. # [2021-09-15 12:41:53 +0800] [13030] [INFO] Starting gunicorn 20.1.0
  3. # [2021-09-15 12:41:53 +0800] [13030] [INFO] Listening at: http://127.0.0.1:4000 (13030)
  4. # [2021-09-15 12:41:53 +0800] [13030] [INFO] Using worker: sync
  5. # [2021-09-15 12:41:53 +0800] [13032] [INFO] Booting worker with pid: 13032
  6. # [2021-09-15 12:41:53 +0800] [13033] [INFO] Booting worker with pid: 13033
  7. # [2021-09-15 12:41:53 +0800] [13034] [INFO] Booting worker with pid: 13034
  8. # [2021-09-15 12:41:53 +0800] [13035] [INFO] Booting worker with pid: 13035

此时,服务已经可以正常启动起来了。

配置文件说明

Gunicorn 服务器在启动时,会依次从上到下尝试读取相关配置(优先级依次降低):

  1. 命令行参数。
  2. 环境变量GUNICORN_CMD_ARGS中设置的命令行参数。
  3. 在当前工作目录中或使用命令行参数指定的可选配置文件gunicorn.conf.py。
  4. 从特定于框架的配置文件中读取配置。
  5. 读取基础环境变量。

Ps: gunicorn 可以使用如下命令来查询当前解析到的配置:

  1. gunicorn --print-config APP_MODULE

关于 gunicorn 支持哪些命令行参数,可以使用如下命令进行查询:

  1. gunicorn -h

而 gunicorn 的配置文件则是一个以 .py 为扩展名的 Python 源文件(默认为 gunicorn.conf.py),在每次启动 Gunicorn 服务时,该配置文件都会被执行,示例如下:

  1. import multiprocessing
  2. bind = "127.0.0.1:8000"
  3. workers = multiprocessing.cpu_count() * 2 + 1

下面,我们给出一个常用的 Gunicorn 的配置文件示例:

  1. import multiprocessing
  2. bind = "0.0.0.0:8000"
  3. workers = multiprocessing.cpu_count() * 2 + 1
  4. pidfile = "gunicorn.pid"
  5. daemon = "true"
  6. accesslog = 'gunicorn_acess.log'
  7. errorlog = 'gunicorn_error.log'
  8. loglevel = 'warning'
  9. access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'

nginx 代理

在生产环境下,我们强烈建议将 Gunicorn 作为一个反向代理的后端服务。
例如,Gunicorn 前应该部署一个 Nginx 来接收请求流量。
一个示例的 Nginx 配置如下:

  1. worker_processes 1;
  2. user nobody nogroup;
  3. error_log /var/log/nginx/error.log warn;
  4. pid /var/run/nginx.pid;
  5. events {
  6. worker_connections 1024; # increase if you have lots of clients
  7. accept_mutex off; # set to 'on' if nginx worker_processes > 1
  8. # 'use epoll;' to enable for Linux 2.6+
  9. }
  10. http {
  11. include mime.types;
  12. default_type application/octet-stream;
  13. access_log /var/log/nginx/access.log combined;
  14. sendfile on;
  15. upstream app_server {
  16. # fail_timeout=0 means we always retry an upstream even if it failed to return a good HTTP response
  17. # for UNIX domain socket setups
  18. server unix:/tmp/gunicorn.sock fail_timeout=0;
  19. # for a TCP configuration
  20. # server 192.168.0.7:8000 fail_timeout=0;
  21. }
  22. server {
  23. # if no Host match, close the connection to prevent host spoofing
  24. listen 80 default_server;
  25. return 444;
  26. }
  27. server {
  28. # use 'listen 80 deferred;' for Linux
  29. # use 'listen 80 accept_filter=httpready;' for FreeBSD
  30. listen 80;
  31. client_max_body_size 4G;
  32. # set the correct host(s) for your site
  33. server_name example.com www.example.com;
  34. keepalive_timeout 5;
  35. # path for static files
  36. root /path/to/app/current/public;
  37. location / {
  38. # checks for static file, if not found proxy to app
  39. try_files $uri @proxy_to_app;
  40. }
  41. location @proxy_to_app {
  42. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  43. proxy_set_header X-Forwarded-Proto $scheme;
  44. proxy_set_header Host $http_host;
  45. # we don't want nginx trying to do something clever with
  46. # redirects, we set the Host: header above already.
  47. proxy_redirect off;
  48. proxy_pass http://app_server;
  49. }
  50. error_page 500 502 503 504 /500.html;
  51. location = /500.html {
  52. root /path/to/app/current/public;
  53. }
  54. }
  55. }

信号处理

接下来,我们介绍一下 Gunicorn 服务常用的一些接收信号,主要围绕它的 Master 进程来说明:

  • QUIT/INT:快速退出
  • TERM:优雅退出
  • HUP:重新加载配置,使用新配置启动新的工作进程并优雅地关闭旧的工作进程。 如果应用程序没有预加载(使用 preload_app 选项),Gunicorn 也会加载它的新版本。
  • USR1:重新打开一个新的日志文件

例如,我们更新了应用程序的代码后,可以执行如下命令来重新启动服务:

  1. kill -HUP `cat gunicorn.pid`