Gunicorn/Uvicorn/WSGI/Nginx乱七八糟的关系
总览
不废话, 先看表格
| 组件 | 协议(向上提供服务) | 协议(向下转发) | 备注 | 
|---|---|---|---|
| Nginx | HTTP | HTTP, uwsgi | unix socket对位的是TCP | 
| Gunicorn | HTTP | WSGI | |
| uWSGI | HTTP, uwsgi | WSGI | 太老旧, 废弃 uWSGI是一个服务器, uwsgi是他的协议 Nginx的uwsgi_pass可对接  | 
    
| Uvicorn | HTTP | ASGI | |
| Django/Flask | WSGI | WSGI  is a Python standard, 不是通信 直接进程内import, 函数调用 另外Django自带一个开发用http服务  | 
    |
| FastAPI(Starlette) | ASGI | ASGI也一样, 区别是异步 | 
根据表格中的协议, 以上组件可以组合出n种部署方案.
同步应用部署方案:
- 🚧Django (runserver基于wsgiref)
 - ❌Gunicorn → Django
 - ✅Nginx → Gunicorn → Django
 - ❌uWSGI → Django
 - ❌Nginx → uWSGI → Django
 
异步应用部署方案:
- 🚧Uvicorn → FastAPI
 - ❌Gunicorn → Uvicorn → FastAPI
 - ❌Nginx → Uvicorn → FastAPI
 - ✅Nginx → Gunicorn → Uvicorn → FastAPI
 
🚧是本地开发调试用的 ✅是推荐的线上方案 ❌是不推荐
真的挺混乱的, 其实还有更多类似的组件和库没放进去呢.
分类和简化一下上述概念可以得出, 走网络的通信协议:
- HTTP
 - uwsgi
 
进程内部python函数调用标准:
- WSGI
 - ASGI
 
Gunicorn
如果是基于WSGI的同步应用, Gunicorn是最佳选项. Gunicorn提供的核心功能
- 负责接受和解析HTTP
 - WSGI协议支持
 - 多worker进程管理(pre-fork worker model)
 
Gunicorn和Django
- worker进程就和Django是同一个进程
 - Gunicorn和Django之间是WSGI, 不用走网络请求了
 - 多个进程监听同一端口? 是的. 通过共享socket或者reuseport功能.
 
WSGI
一个简单函数:
def simple_app(environ, start_response):
    status = '200 OK'
    headers = [('Content-Type', 'text/plain')]
    start_response(status, headers)
    return [b"Hello, WSGI!"]
# 使用 wsgiref 内置服务器运行
from wsgiref.simple_server import make_server
httpd = make_server('', 8000, simple_app)
print("Serving on port 8000...")
httpd.serve_forever()
说明:
- environ: 一个包含请求信息的字典(如路径、请求方法等)
 - start_response(status, headers): 一个函数,用于发送HTTP响应状态和头部信息
 - 返回值:必须是一个 可迭代对象,其中每个元素是bytes
 
Nginx和Gunicorn
- 
    
Gunicorn独自就能干活了, 为啥要加一层Nginx?
根据SO上的问题, 大部分生产级复杂系统, 都有很多不应该交由Python来响应的请求, 如静态资源, 这就需要加一层Nginx.
 - 
    
Nginx会不会将请求转换为wsgi再发给应用服务呢?
不会, Nginx又不是python, 调用不了python方法, 没实现wsgi; uwsgi一字之差啊;
 - 
    
Nginx和Gunicorn之间的通信?
简单的HTTP转发关系, 同一台机器上使用unix socket连接, unix socket对位的是TCP.
 - 
    
他俩谁会解析HTTP请求?
都会. Nginx出于转发规则的目的, 也要知道HTTP请求路径之类的信息. Gunicorn则要完整解析HTTP请求, 之后通过WSGI转给Django.
 
Nginx → Gunicorn → Django 完整流程图👍🏻
 graph TD
    a1["Django"]
    a2["Django"]
    a3["Django"]
    Client -->|HTTP| Nginx
    subgraph Nginx
		    Port-80 -->|HTTP| NginxWorker-1
		    Port-80 -->|HTTP| NginxWorker-2
		    Port-80 -->|HTTP| NginxWorker-N
    end
    Nginx -->|HTTP| Gunicorn
    subgraph Gunicorn
		    Port-8080 -->|HTTP| GunicornSyncWorker-1
		    Port-8080 -->|HTTP| GunicornSyncWorker-2
		    Port-8080 -->|HTTP| GunicornSyncWorker-N
        subgraph "Gunicorn-N"
            GunicornSyncWorker-N -->|WSGI| a3
        end
        subgraph "Gunicorn-2"
            GunicornSyncWorker-2 -->|WSGI| a2
        end
        subgraph "Gunicorn-1"
            GunicornSyncWorker-1 -->|WSGI| a1
        end
    end
    desc1["实线矩形是进程"]
style desc1 fill:#fff,stroke:#fff,color:#555,font-style:italic
style Port-8080 fill:#f0f0f0,stroke:#ccc,stroke-dasharray: 5 5,color:#999
style Port-80 fill:#f0f0f0,stroke:#ccc,stroke-dasharray: 5 5,color:#999
style Gunicorn stroke:#ccc,stroke-dasharray: 5 5,color:#999
style Nginx stroke:#ccc,stroke-dasharray: 5 5,color:#999
style GunicornSyncWorker-1 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style GunicornSyncWorker-2 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style GunicornSyncWorker-N stroke:#ccc,stroke-dasharray: 5 5,color:#999
style a1 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style a2 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style a3 stroke:#ccc,stroke-dasharray: 5 5,color:#999
Uvicorn
实现了ASGI的应用服务器
ASGI
一个async函数
async def app(scope, receive, send):
    if scope["type"] == "http":
        event = await receive()
        body = event.get("body", b"")
        await send({
            "type": "http.response.start",
            "status": 200,
            "headers": [(b"content-type", b"text/plain")],
        })
        await send({
            "type": "http.response.body",
            "body": body,
        })
说明:
receive: 等待客户端发送事件的协程函数send: 向客户端发送事件的协程函数- 
    
scope: 请求的上下文信息(如类型、路径、headers){ "type": "http", "method": "GET", "path": "/", "headers": [(b"host", b"example.com")] } 
| 特性 | WSGI | ASGI | 
|---|---|---|
| 是否异步支持 | ❌ 不支持(同步阻塞) | ✅ 原生支持异步与并发 | 
| 支持 WebSocket | ❌ | ✅ 支持 | 
| 服务端模型 | 线程/进程 | 协程(事件循环) | 
Uvicorn和Gunicorn
Uvicorn已经能提供http服务了, 为啥还用Gunicorn:
- Gunicorn 的进程管理能力: Gunicorn 具有强大的进程管理能力,可以监控 worker 进程的运行状态,并在进程崩溃时自动重启。这对于保证应用的稳定性和可靠性非常有帮助。
 - Uvicorn 的高性能: Uvicorn 在处理异步应用时具有很高的性能,可以有效地提高应用的吞吐量和响应速度。
 
Nginx → Gunicorn → Uvicorn → FastAPI 完整流程图👍🏻
graph TD
    a1["FastAPI"]
    a2["FastAPI"]
    a3["FastAPI"]
    Client -->|HTTP| Nginx
    subgraph Nginx
		    Port-80 -->|HTTP| NginxWorker-1
		    Port-80 -->|HTTP| NginxWorker-2
		    Port-80 -->|HTTP| NginxWorker-N
    end
    Nginx -->|HTTP| Gunicorn
    subgraph Gunicorn
		    Port-8080 -->|HTTP| UvicornWorker-1
		    Port-8080 -->|HTTP| UvicornWorker-2
		    Port-8080 -->|HTTP| UvicornWorker-N
        subgraph "Gunicorn-N"
            UvicornWorker-N -->|ASGI| a3
        end
        subgraph "Gunicorn-2"
            UvicornWorker-2 -->|ASGI| a2
        end
        subgraph "Gunicorn-1"
            UvicornWorker-1 -->|ASGI| a1
        end
    end
    desc1["实线矩形是进程"]
style desc1 fill:#fff,stroke:#fff,color:#555,font-style:italic
style Port-8080 fill:#f0f0f0,stroke:#ccc,stroke-dasharray: 5 5,color:#999
style Port-80 fill:#f0f0f0,stroke:#ccc,stroke-dasharray: 5 5,color:#999
style Gunicorn stroke:#ccc,stroke-dasharray: 5 5,color:#999
style Nginx stroke:#ccc,stroke-dasharray: 5 5,color:#999
style UvicornWorker-1 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style UvicornWorker-2 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style UvicornWorker-N stroke:#ccc,stroke-dasharray: 5 5,color:#999
style a1 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style a2 stroke:#ccc,stroke-dasharray: 5 5,color:#999
style a3 stroke:#ccc,stroke-dasharray: 5 5,color:#999
| 距离上一篇文章又隔了一年多了啊, 又在瞎忙, 没啥内容产出 |