mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-01 10:11:30 +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
|
||||
- Configure: configure.md
|
||||
- Deploy: deploy.md
|
||||
- ASGI Worker: asgi.md
|
||||
- uWSGI Protocol: uwsgi.md
|
||||
- Signals: signals.md
|
||||
- Instrumentation: instrumentation.md
|
||||
- Custom: custom.md
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user