diff --git a/docs/content/asgi.md b/docs/content/asgi.md new file mode 100644 index 00000000..8cc51b4b --- /dev/null +++ b/docs/content/asgi.md @@ -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 diff --git a/docs/content/uwsgi.md b/docs/content/uwsgi.md new file mode 100644 index 00000000..af2b0c75 --- /dev/null +++ b/docs/content/uwsgi.md @@ -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 diff --git a/mkdocs.yml b/mkdocs.yml index d6945086..6a5caf1a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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