mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-02 18:51:31 +08:00
docs: Add ASGI worker and uWSGI protocol documentation
This commit is contained in:
parent
19a2efec63
commit
0a697cde7f
241
docs/content/asgi.md
Normal file
241
docs/content/asgi.md
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
# ASGI Worker
|
||||||
|
|
||||||
|
!!! warning "Beta Feature"
|
||||||
|
The ASGI worker is a beta feature introduced in Gunicorn 24.0.0. While it has been tested,
|
||||||
|
the API and behavior may change in future releases. Please report any issues on
|
||||||
|
[GitHub](https://github.com/benoitc/gunicorn/issues).
|
||||||
|
|
||||||
|
Gunicorn includes a native ASGI worker that enables running async Python web frameworks
|
||||||
|
like FastAPI, Starlette, and Quart without external dependencies like Uvicorn.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install gunicorn
|
||||||
|
pip install gunicorn
|
||||||
|
|
||||||
|
# Run an ASGI application
|
||||||
|
gunicorn myapp:app --worker-class asgi --workers 4
|
||||||
|
```
|
||||||
|
|
||||||
|
For FastAPI applications:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn main:app --worker-class asgi --bind 0.0.0.0:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
The ASGI worker provides:
|
||||||
|
|
||||||
|
- **HTTP/1.1** with keepalive connections
|
||||||
|
- **WebSocket** support for real-time applications
|
||||||
|
- **Lifespan protocol** for startup/shutdown hooks
|
||||||
|
- **Optional uvloop** for improved performance
|
||||||
|
- **SSL/TLS** support
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Worker Class
|
||||||
|
|
||||||
|
Set the worker class to `asgi`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --worker-class asgi
|
||||||
|
```
|
||||||
|
|
||||||
|
Or in a configuration file:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# gunicorn.conf.py
|
||||||
|
worker_class = "asgi"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event Loop
|
||||||
|
|
||||||
|
Control which asyncio event loop implementation to use:
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `auto` | Use uvloop if available, otherwise asyncio (default) |
|
||||||
|
| `asyncio`| Use Python's built-in asyncio event loop |
|
||||||
|
| `uvloop` | Use uvloop (must be installed separately) |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --worker-class asgi --asgi-loop uvloop
|
||||||
|
```
|
||||||
|
|
||||||
|
To use uvloop, install it first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install uvloop
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lifespan Protocol
|
||||||
|
|
||||||
|
The lifespan protocol lets your application run code at startup and shutdown.
|
||||||
|
This is essential for frameworks that need to initialize database connections,
|
||||||
|
caches, or background tasks.
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `auto` | Detect if app supports lifespan, enable if so (default) |
|
||||||
|
| `on` | Always run lifespan protocol (fail if unsupported) |
|
||||||
|
| `off` | Never run lifespan protocol |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --worker-class asgi --asgi-lifespan on
|
||||||
|
```
|
||||||
|
|
||||||
|
Example FastAPI application using lifespan:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
# Startup: initialize resources
|
||||||
|
print("Starting up...")
|
||||||
|
yield
|
||||||
|
# Shutdown: cleanup resources
|
||||||
|
print("Shutting down...")
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Root Path
|
||||||
|
|
||||||
|
When running behind a reverse proxy that mounts your application at a subpath,
|
||||||
|
set `root_path` so your application knows its mount point:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --worker-class asgi --root-path /api
|
||||||
|
```
|
||||||
|
|
||||||
|
This is equivalent to the `SCRIPT_NAME` in WSGI applications.
|
||||||
|
|
||||||
|
### Worker Connections
|
||||||
|
|
||||||
|
Control the maximum number of concurrent connections per worker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --worker-class asgi --worker-connections 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Unlike sync workers, the `--threads` option has no effect on ASGI workers.
|
||||||
|
Use `--worker-connections` to control concurrency.
|
||||||
|
|
||||||
|
## WebSocket Support
|
||||||
|
|
||||||
|
The ASGI worker supports WebSocket connections out of the box. No additional
|
||||||
|
configuration is required.
|
||||||
|
|
||||||
|
Example with Starlette:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
from starlette.routing import WebSocketRoute
|
||||||
|
|
||||||
|
async def websocket_endpoint(websocket):
|
||||||
|
await websocket.accept()
|
||||||
|
while True:
|
||||||
|
data = await websocket.receive_text()
|
||||||
|
await websocket.send_text(f"Echo: {data}")
|
||||||
|
|
||||||
|
app = Starlette(routes=[
|
||||||
|
WebSocketRoute("/ws", websocket_endpoint),
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
### With Nginx
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream gunicorn {
|
||||||
|
server 127.0.0.1:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://gunicorn;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebSocket support
|
||||||
|
location /ws {
|
||||||
|
proxy_pass http://gunicorn;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recommended Settings
|
||||||
|
|
||||||
|
For production ASGI deployments:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# gunicorn.conf.py
|
||||||
|
worker_class = "asgi"
|
||||||
|
workers = 4 # Number of worker processes
|
||||||
|
worker_connections = 1000 # Max connections per worker
|
||||||
|
keepalive = 5 # Keepalive timeout
|
||||||
|
timeout = 120 # Worker timeout
|
||||||
|
graceful_timeout = 30 # Graceful shutdown timeout
|
||||||
|
|
||||||
|
# Performance tuning
|
||||||
|
asgi_loop = "auto" # Use uvloop if available
|
||||||
|
asgi_lifespan = "auto" # Auto-detect lifespan support
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comparison with Other ASGI Servers
|
||||||
|
|
||||||
|
| Feature | Gunicorn ASGI | Uvicorn | Hypercorn |
|
||||||
|
|---------|---------------|---------|-----------|
|
||||||
|
| Process management | Built-in | External | Built-in |
|
||||||
|
| HTTP/2 | No | No | Yes |
|
||||||
|
| WebSocket | Yes | Yes | Yes |
|
||||||
|
| Lifespan | Yes | Yes | Yes |
|
||||||
|
| uvloop support | Yes | Yes | Yes |
|
||||||
|
|
||||||
|
Gunicorn's ASGI worker provides the same process management, logging, and
|
||||||
|
configuration capabilities you're familiar with from WSGI deployments.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Lifespan startup failed
|
||||||
|
|
||||||
|
If you see "ASGI lifespan startup failed", your application may not properly
|
||||||
|
implement the lifespan protocol. Either fix the application or set
|
||||||
|
`--asgi-lifespan off`.
|
||||||
|
|
||||||
|
### Connection limits
|
||||||
|
|
||||||
|
If you're hitting connection limits, increase `--worker-connections` or add
|
||||||
|
more workers with `--workers`.
|
||||||
|
|
||||||
|
### Slow responses under load
|
||||||
|
|
||||||
|
Try using uvloop for better performance:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install uvloop
|
||||||
|
gunicorn myapp:app --worker-class asgi --asgi-loop uvloop
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
- [Settings Reference](reference/settings.md#asgi_loop) - All ASGI-related settings
|
||||||
|
- [Deploy](deploy.md) - General deployment guidance
|
||||||
|
- [Design](design.md) - Worker architecture overview
|
||||||
266
docs/content/uwsgi.md
Normal file
266
docs/content/uwsgi.md
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
# uWSGI Protocol
|
||||||
|
|
||||||
|
Gunicorn supports the uWSGI binary protocol, allowing it to receive requests from
|
||||||
|
nginx using the `uwsgi_pass` directive. This provides efficient communication
|
||||||
|
between nginx and Gunicorn without HTTP overhead.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
This is the **uWSGI binary protocol**, not the uWSGI server. Gunicorn
|
||||||
|
implements the protocol to receive requests from nginx, similar to how
|
||||||
|
the uWSGI server would.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Enable uWSGI protocol support:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --protocol uwsgi --bind 127.0.0.1:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure nginx to forward requests:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream gunicorn {
|
||||||
|
server 127.0.0.1:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
uwsgi_pass gunicorn;
|
||||||
|
include uwsgi_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why Use uWSGI Protocol?
|
||||||
|
|
||||||
|
The uWSGI binary protocol offers several advantages over HTTP proxying:
|
||||||
|
|
||||||
|
- **Lower overhead** - Binary format is more compact than HTTP headers
|
||||||
|
- **Better integration** - nginx's native uwsgi module is highly optimized
|
||||||
|
- **Simpler configuration** - No need to reconstruct HTTP headers
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Protocol Setting
|
||||||
|
|
||||||
|
Switch from HTTP to uWSGI protocol:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --protocol uwsgi
|
||||||
|
```
|
||||||
|
|
||||||
|
Or in a configuration file:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# gunicorn.conf.py
|
||||||
|
protocol = "uwsgi"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Allowed IPs
|
||||||
|
|
||||||
|
By default, uWSGI protocol requests are only accepted from localhost
|
||||||
|
(`127.0.0.1` and `::1`). This prevents unauthorized hosts from sending
|
||||||
|
requests directly to Gunicorn.
|
||||||
|
|
||||||
|
To allow additional IPs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --protocol uwsgi --uwsgi-allow-from 10.0.0.1,10.0.0.2
|
||||||
|
```
|
||||||
|
|
||||||
|
To allow all IPs (not recommended for production):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --protocol uwsgi --uwsgi-allow-from '*'
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Only allow IPs from trusted sources. The uWSGI protocol does not provide
|
||||||
|
authentication, so anyone who can connect can send requests.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
UNIX socket connections are always allowed regardless of this setting.
|
||||||
|
|
||||||
|
### Using UNIX Sockets
|
||||||
|
|
||||||
|
For better performance and security, use UNIX sockets instead of TCP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunicorn myapp:app --protocol uwsgi --bind unix:/run/gunicorn.sock
|
||||||
|
```
|
||||||
|
|
||||||
|
Nginx configuration:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream gunicorn {
|
||||||
|
server unix:/run/gunicorn.sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
uwsgi_pass gunicorn;
|
||||||
|
include uwsgi_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nginx Configuration
|
||||||
|
|
||||||
|
### Basic Setup
|
||||||
|
|
||||||
|
Create or verify the `uwsgi_params` file exists (usually at `/etc/nginx/uwsgi_params`):
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
uwsgi_param QUERY_STRING $query_string;
|
||||||
|
uwsgi_param REQUEST_METHOD $request_method;
|
||||||
|
uwsgi_param CONTENT_TYPE $content_type;
|
||||||
|
uwsgi_param CONTENT_LENGTH $content_length;
|
||||||
|
|
||||||
|
uwsgi_param REQUEST_URI $request_uri;
|
||||||
|
uwsgi_param PATH_INFO $document_uri;
|
||||||
|
uwsgi_param DOCUMENT_ROOT $document_root;
|
||||||
|
uwsgi_param SERVER_PROTOCOL $server_protocol;
|
||||||
|
uwsgi_param REQUEST_SCHEME $scheme;
|
||||||
|
uwsgi_param HTTPS $https if_not_empty;
|
||||||
|
|
||||||
|
uwsgi_param REMOTE_ADDR $remote_addr;
|
||||||
|
uwsgi_param REMOTE_PORT $remote_port;
|
||||||
|
uwsgi_param SERVER_PORT $server_port;
|
||||||
|
uwsgi_param SERVER_NAME $server_name;
|
||||||
|
```
|
||||||
|
|
||||||
|
### With SSL Termination
|
||||||
|
|
||||||
|
When nginx handles SSL and forwards to Gunicorn:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
ssl_certificate /path/to/cert.pem;
|
||||||
|
ssl_certificate_key /path/to/key.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
uwsgi_pass gunicorn;
|
||||||
|
include uwsgi_params;
|
||||||
|
uwsgi_param HTTPS on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Load Balancing
|
||||||
|
|
||||||
|
Distribute requests across multiple Gunicorn instances:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream gunicorn {
|
||||||
|
least_conn;
|
||||||
|
server 127.0.0.1:8000;
|
||||||
|
server 127.0.0.1:8001;
|
||||||
|
server 127.0.0.1:8002;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
uwsgi_pass gunicorn;
|
||||||
|
include uwsgi_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Files
|
||||||
|
|
||||||
|
Serve static files directly from nginx:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /path/to/static/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
uwsgi_pass gunicorn;
|
||||||
|
include uwsgi_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protocol Details
|
||||||
|
|
||||||
|
The uWSGI protocol uses a compact binary format:
|
||||||
|
|
||||||
|
| Bytes | Field | Description |
|
||||||
|
|-------|-------|-------------|
|
||||||
|
| 0 | modifier1 | Packet type (0 = WSGI request) |
|
||||||
|
| 1-2 | datasize | Size of vars block (little-endian) |
|
||||||
|
| 3 | modifier2 | Additional flags (usually 0) |
|
||||||
|
|
||||||
|
After the header, the vars block contains CGI-style key-value pairs:
|
||||||
|
|
||||||
|
```
|
||||||
|
[2-byte key_size][key][2-byte val_size][value]...
|
||||||
|
```
|
||||||
|
|
||||||
|
Standard CGI variables like `REQUEST_METHOD`, `PATH_INFO`, and `QUERY_STRING`
|
||||||
|
are extracted from this block to construct the WSGI environ.
|
||||||
|
|
||||||
|
## Combining with HTTP
|
||||||
|
|
||||||
|
You can run Gunicorn with both HTTP and uWSGI protocol support by running
|
||||||
|
separate instances:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# HTTP for direct access
|
||||||
|
gunicorn myapp:app --bind 127.0.0.1:8080
|
||||||
|
|
||||||
|
# uWSGI for nginx
|
||||||
|
gunicorn myapp:app --protocol uwsgi --bind 127.0.0.1:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### ForbiddenUWSGIRequest Error
|
||||||
|
|
||||||
|
If you see "Forbidden uWSGI request from IP", the connecting IP is not in
|
||||||
|
the allowed list. Either:
|
||||||
|
|
||||||
|
1. Add the IP to `--uwsgi-allow-from`
|
||||||
|
2. Use UNIX sockets instead
|
||||||
|
3. Ensure nginx is connecting from an allowed IP
|
||||||
|
|
||||||
|
### Invalid uWSGI Header
|
||||||
|
|
||||||
|
This usually means:
|
||||||
|
|
||||||
|
1. HTTP traffic is being sent to a uWSGI endpoint
|
||||||
|
2. The packet is malformed or truncated
|
||||||
|
3. Network issues caused data corruption
|
||||||
|
|
||||||
|
Verify that nginx is using `uwsgi_pass` (not `proxy_pass`) and that the
|
||||||
|
`uwsgi_params` file is being included.
|
||||||
|
|
||||||
|
### Headers Missing
|
||||||
|
|
||||||
|
If certain headers aren't reaching your application, verify they're included
|
||||||
|
in `uwsgi_params`. Custom headers should be passed as:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
uwsgi_param HTTP_X_CUSTOM_HEADER $http_x_custom_header;
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
- [Settings Reference](reference/settings.md#protocol) - Protocol and uWSGI settings
|
||||||
|
- [Deploy](deploy.md) - General deployment guidance
|
||||||
|
- [Design](design.md) - Worker architecture overview
|
||||||
@ -12,6 +12,8 @@ nav:
|
|||||||
- Run: run.md
|
- Run: run.md
|
||||||
- Configure: configure.md
|
- Configure: configure.md
|
||||||
- Deploy: deploy.md
|
- Deploy: deploy.md
|
||||||
|
- ASGI Worker: asgi.md
|
||||||
|
- uWSGI Protocol: uwsgi.md
|
||||||
- Signals: signals.md
|
- Signals: signals.md
|
||||||
- Instrumentation: instrumentation.md
|
- Instrumentation: instrumentation.md
|
||||||
- Custom: custom.md
|
- Custom: custom.md
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user