41-Gunicorn 原理
- gunicron 官网:https://docs.gunicorn.org/en/stable/
- github 地址:https://github.com/benoitc/gunicorn
Gunicorn 工作原理
Gunicorn 使用 pre-fork worker 模型,这意味着在处理请求之前,它会预先 fork 出多个 worker 进程。每个 worker 进程都是独立的,可以在不同的 CPU 核心上运行。这种模型的优点在于:
- 隔离性好:每个 worker 进程都是独立的,如果一个进程崩溃,不会影响其他进程。
- 扩展性强:可以根据需要增加或减少worker进程的数量。
- 性能优越:可以充分利用多核CPU的并行处理能力。
总体结构
gunicorn pre-fork worker 模型中有一个管理进程以及几个的工作进程。管理进程:master,工作进程:worker。(以下代码中为了方面理解,均去除了一些干扰代码)。
启动之后,manager 维护数量固定的 worker:
1 |
|
master 通过 pre-fork 的方式创建多个 worker:
1 |
|
在 worker.init_process()
函数中,worker 中 gunicorn 的 app 对象会去 import 我们的 wsgi app。也就是说,每个 woker 子进程都会单独去实例化我们的 wsgi app 对象。每个 worker 中的 swgi app 对象是相互独立、互不干扰的。
创建完所有的 worker 后,worker 和 master 各自进入自己的消息循环。
- master 的事件循环就是收收信号,管理管理 worker 进程
- worker 进程的事件循环就是监听网络事件并处理(如新建连接,断开连接,处理请求发送响应等等),所以真正的连接最终是连到了 worker 进程上的。(注:有关这种多进程模型的详细介绍,可以参考http://blog.csdn.net/largetalk/article/details/7939080)
worker
woker 有很多种,包括:ggevent、geventlet、gtornado、gthread 等等。这里主要分析 ggevent。
每个 ggevent worker 启动的时候会启动多个 server 对象:worker 首先为每个 listener 创建一个 server 对象(注:为什么是一组 listener,因为 gunicorn 可以绑定一组地址,每个地址对于一个 listener),每个 server 对象都有运行在一个单独的 gevent pool 对象中。真正等待链接和处理链接的操作是在 server 对象中进行的。
1 |
|
上面代码中的 server_class
实际上是一个 gevent 的 WSGI SERVER 的子类:
1 |
|
WSGI SERVER
真正等待链接和处理链接的操作是在 gevent 的 WSGIServer 和 WSGIHandler 中进行的。
最后再来看一下 gevent 的 WSGIServer 和 WSGIHandler 的主要实现:
WSGIServer 的 start 函数里面调用 start_accepting
来处理到来的链接。在 start_accepting
里面得到接收到的套接字后调用 do_handle
来处理套接字:
1 |
|
可以看出,WSGIServer 实际上是创建一个协程去处理该套接字,也就是说在 WSGIServer 中,一个协程单独负责一个 HTTP 链接。协程中运行的 self._handle
函数实际上是调用了 WSGIHandler 的 handle 函数来不断处理 HTTP 请求:
1 |
|
在 handle 函数的循环内部,handle_one_request
函数首先读取 HTTP 请求,初始化 WSGI 环境,然后最终调用 run_application
函数来处理请求:
1 |
|
在这个地方才真正的调用了我们的 app。
总结:gunicorn 会启动一组 worker 进程,所有 worker 进程公用一组 listener,在每个 worker 中为每个 listener 建立一个 wsgi server。每当有 HTTP 链接到来时,wsgi server 创建一个协程来处理该链接,协程处理该链接的时候,先初始化 WSGI 环境,然后调用用户提供的 app 对象去处理HTTP 请求。