gunicorn
Gunicorn – unix 上被广泛使用的高性能 Python WSGI UNIX HTTP Server,常用于生产环境中运行的 Python Web 应用程序。
它所在的位置通常是在反向代理(Nginx)或者 负载均衡 和一个 web 应用之间。它是一个移植自 Ruby 的 Unicorn 项目的 pre-woker 模型,即支持 eventlet 也支持 greenlet
工作原理
Gunicorn 基于 pre-fork worker 模型。这意味着有一个中央主进程管理一组工作进程。
主进程是一个简单的循环,它监听各种进程信号并做出相应的反应。它通过监听 TTIN、TTOUT 和 CHLD 等信号来管理正在运行的工作人员列表。TTIN 和 TTOUT 告诉 master 增加或减少正在运行的 worker 的数量。CHLD 表示一个子进程已经终止,在这种情况下主进程会自动重启失败的工作进程。
特点
- WSGI 兼容性:Gnicorn 符合 WSGI 规范,定义了 Web 服务器和 Python 应用程序之间通信的标准接口。这使得 Gunicorn 与许多 Python Web 框架(如 Django\Flask等)兼容。
- 高性能:gunicorn 使用异步的、事件驱动的编程模型,具备大量的并发连接。采用 pre-fork 的方式,每个进程可以处理多个请求,并在请求到达时快速响应。
- 丰富的配置:gunicorn 提供了丰富的配置选项、可以通过配置文件或者命令行参数进行调整。你可以指定工作进的数量、绑定的主机和端口、日志级别等。
- 进程管理:gunicorn 具有内置的进程管理功能,可以管理工作进程的生命周期。它可以自动重启崩溃的进程,并在需要时平滑地进行热部署,而不会中断正在处理的请求。
- 安全性:Gunicorn 提供了一些安全机制,包括 Unix 权限控制、访问日志记录和访问限制。它还可以与反向代理(Nginx)结合使用,以增强安全性和性能
- 插件生态系统:Gunicorn 支持多插件,可以扩展其功能。例如,有插件用于加载静态文件、实现 SSL 加密、处理 WebSocket 连接等。
常规使用
常规参数一览
https://docs.gunicorn.org/en/stable/run.html#commonly-used-arguments
- 绑定地址和端口相关配置
ip:port
backlog
: 定义等待连接队列的最大长度,在 TCP/IP 网络编程中,当一个服务器正在处理并发连接请求时,如果有更多的连接到达,当服务器当前无法及时处理时,这些请求将会被放置在一个等待连接队列中,等待服务器处理。
1 | # 绑定端口 注意0.0.0.0的配置在docker网络中是关键配置 |
- 工作进程和并发性能配置
workers
: 定义工作进程的数量,可以是整数值或者是 CPU 核心数的倍数worker_class
: 定义 worker 类型,例如:sync\gevent\eventlet 等worker_connections
: 每个工作进程允许的最大并发连接数threads
: 每个工作进程中的线程数(仅适用于支持线程的工作进程)
1 | # 工作进程数 常规使用Docker横向扩容,故按项目实际情况配置 |
- 进程管理和重启配置
max_requests
: 每个工作进程处理的最大请求数,达到后将自动重启max_requests_jitter
: 请求处理最大请求数的抖动范围timeout
: 工作进程处理请求的超时时间graceful_timeout
: 优雅关闭工作进程的超时时间
1 | # 3. 进程管理和重启配置 -> max_requests && max_requests_jitter && timeout && graceful_timeout |
- 日志和调试配置
accesslog
: 访问日志文件的路径,或者使用特定的值(例如-
)输出到标准输出errorlog
: 错误日志文件的路径,或者使用特定的值(例如-
)输入到标准输出loglevel
: 日志记录级别,例如degub
,info
,warning
1 | # 是否使用debug模式 |
1 | CMD ["gunicorn", "-c", "gunicorn.conf.py", "server:create_app()", "–access-logfile -"] |
how to choose worker
参考:https://docs.gunicorn.org/en/stable/design.html#choosing-a-worker-type
如何选择一个适用的 worker, Gunicorn 提供了多种类型的工作进程(Workers),每种
工作进程都有不同的工作模型和适用场景。以下是 Gunicorn 支持的常见工作类型:
Sync - 同步工作进程
sync
默认的工作进程类型。它使用同步的方式处理请求,即每个工作进程一次只处理一个请求。适用于 IO 密集型的应用程序,但是在面对长时间运行的请求或高并发情况下可能会出现性能问题。Async workers - 异步工作进程
gevent
: 使用 gevent 库作为异步处理引擎。适用于 IO 密集型的应用程序,能够处理大量的并发连接,提高性能。eventlet
: 使用eventlet 库
作为异步处理引擎。类似于 gevent,适用于 IO 密集型的应用程序,具有良好的并发性能。- ……
Thread-based Workers (基于线程的工作进程)
sync
: 使用多线程来处理请求。适用于处理计算密集型的任务,可以利用多核 CPU 的优势,但在面对 IO 密集型的应用程序时可能会有性能问题
gevent 介绍
gevent 是一个 Python 网络函数库,通过 Greenlet 协程 + libev 快速事件循环,实现了异步模型。
gevent 的主要特定和工作原理如下:
- 协程 (Greenlet):gevent 使用 greenlet 库实现协程。协程是一种用户级线程,可以在同一个线程中切换执行。每个协程都有自己的执行上下文和栈,可以独立地运行和暂停,以及在适当的时机切换到其他协程。
- 事件循环(Event Loop):gevent 使用事件循环机制来调度协程的执行。事件循环负责管理协程的调度和协程之间的切换。当一个协程被阻塞时(例如等待 I/O 操作完成),事件循环会切换到其他可运行的协程,以避免阻塞整个程序。
- 非阻塞式 I/O:gevent 利用非阻塞式 I/O 来实现协程之间的切换。当一个协程遇到 I/O 操作时(例如网络请求或文件读写),它会将 I/O 操作交给 gevent 的事件循环处理,并切换到其他协程继续执行。当 I/O 操作完成时,事件循环会通知相应的协程,让器继续执行。
- 基于 Monkey patching 的协程化:gevent 通过 Monkey Patching 技术,对 Python 标准库中的一些阻塞式 I/O 操作进行替换,使其变成非阻塞的。
gevent 如何实现并发
在 gevent 中,每个请求的连接都是一个 Greenlet 协程。Gevent 虽然只有一个线程、同时只能处理一个请求,但是在这个请求的异步事件没准备好时,进入到 IO 等待时,能主动 yield 让出控制权、而不是阻塞其他请求的协程,而是先让其他协程执行,当自己的 IO 准备好后,事件循环会将它从 yield 让出控制权的地方,继续恢复执行。
这样,Gevent 就能在不同的请求间不断切换,从而实现并发,以充分利用 CPU、减少 IO 等待时间。并且,切换的 Greenlet 是微线程,它操作的维度是函数,而不是线程/进程,所以来回切换的开销,就没有那么大。
同步 worker 和 异步 worker ,这两种 worker 类型是最常用的。一般而言,对于我们的应用而言,我们的 web app 多半属于 外部 IO 密集型(总要访问第三方服务等),所以用 Gunicorn 的 Gevent 异步 worker,就非常合理。
而如果你的 web app 是 CPU 密集型,或者你希望请求之间不要互相影响,那么可以选择 Gunicorn 的 同步 worker。
常见问题
常见问题:https://docs.gunicorn.org/en/stable/faq.html#faq
参考
https://stackoverflow.com/questions/36834234/eventlet-vs-greenlet-vs-gevent
https://docs.gunicorn.org/en/latest/design.html#how-many-workers
https://bbs.huaweicloud.com/blogs/309794
https://docs.gunicorn.org/en/stable/design.html#choosing-a-worker-type