301 lines
7.2 KiB
Markdown
301 lines
7.2 KiB
Markdown
# 多实例与负载均衡
|
||
|
||
:::tip 说明
|
||
当单台服务器无法满足性能需求时,需要通过多实例部署和负载均衡来提升系统吞吐量。本文介绍本项目的水平扩展方案。
|
||
:::
|
||
|
||
## Gunicorn 多 Worker 配置
|
||
|
||
### Worker 数量计算
|
||
|
||
```python
|
||
# gunicorn.conf.py
|
||
import multiprocessing
|
||
|
||
# 推荐公式:CPU 核心数 * 2 + 1
|
||
workers = multiprocessing.cpu_count() * 2 + 1
|
||
|
||
# Worker 类型
|
||
worker_class = 'gevent' # 异步 Worker,适合 I/O 密集型
|
||
|
||
# 超时时间
|
||
timeout = 120
|
||
|
||
# 最大请求数(防止内存泄漏)
|
||
max_requests = 1000
|
||
max_requests_jitter = 50
|
||
|
||
# 绑定地址
|
||
bind = '127.0.0.1:8000'
|
||
```
|
||
|
||
### Worker 类型选择
|
||
|
||
| 类型 | 说明 | 适用场景 |
|
||
|------|------|---------|
|
||
| `sync` | 同步 Worker(默认) | CPU 密集型 |
|
||
| `gevent` | 异步 Worker | I/O 密集型(推荐) |
|
||
| `uvicorn` | ASGI Worker | 异步框架 |
|
||
|
||
## Nginx 负载均衡
|
||
|
||
### 基础配置
|
||
|
||
```nginx
|
||
http {
|
||
upstream djangoadmin {
|
||
# 轮询策略(默认)
|
||
server 127.0.0.1:8001;
|
||
server 127.0.0.1:8002;
|
||
server 127.0.0.1:8003;
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
server_name your-domain.com;
|
||
|
||
# 前端静态文件
|
||
root /opt/project/ui/dist;
|
||
index index.html;
|
||
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
# 后端 API 负载均衡
|
||
location /api/ {
|
||
proxy_pass http://djangoadmin;
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 负载均衡策略
|
||
|
||
| 策略 | 配置 | 说明 |
|
||
|------|------|------|
|
||
| 轮询 | 默认 | 依次分配请求 |
|
||
| 权重 | `weight=3` | 按权重分配 |
|
||
| IP Hash | `ip_hash` | 同一 IP 固定到同一后端 |
|
||
| 最少连接 | `least_conn` | 分配到连接数最少的后端 |
|
||
|
||
```nginx
|
||
upstream djangoadmin {
|
||
# 权重策略:服务器配置不同按权重分配
|
||
server 127.0.0.1:8001 weight=3;
|
||
server 127.0.0.1:8002 weight=2;
|
||
server 127.0.0.1:8003 weight=1;
|
||
|
||
# IP Hash:需要 Session 粘性时使用
|
||
# ip_hash;
|
||
|
||
# 最少连接
|
||
# least_conn;
|
||
|
||
# 健康检查
|
||
server 127.0.0.1:8001 max_fails=3 fail_timeout=30s;
|
||
}
|
||
```
|
||
|
||
## JWT 无状态认证
|
||
|
||
项目使用 JWT 认证,天然支持多实例部署:
|
||
|
||
```
|
||
用户请求 → Nginx → 任意后端实例 → 验证 JWT → 返回响应
|
||
```
|
||
|
||
### 为什么 JWT 适合多实例
|
||
|
||
```
|
||
1. 无状态:Token 自包含用户信息,无需服务端 Session
|
||
2. 无需共享存储:不需要 Redis 共享 Session
|
||
3. 任意路由:请求可以发到任意后端实例
|
||
4. 水平扩展:增加实例即可提升性能
|
||
```
|
||
|
||
:::warning Token 黑名单
|
||
退出登录时 Token 加入 Redis 黑名单。多实例部署时,所有实例必须连接同一个 Redis,否则 Token 黑名单不生效。
|
||
:::
|
||
|
||
## 文件存储共享
|
||
|
||
多实例部署时,上传的文件需要所有实例都能访问:
|
||
|
||
### 方案 1:NFS 共享存储
|
||
|
||
```bash
|
||
# 服务器挂载 NFS
|
||
mount -t nfs storage-server:/data/uploads /opt/project/public/uploads
|
||
```
|
||
|
||
### 方案 2:对象存储(推荐)
|
||
|
||
使用 OSS/S3/MinIO 等对象存储:
|
||
|
||
```python
|
||
# 配置对象存储
|
||
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||
AWS_STORAGE_BUCKET_NAME = 'djangoadmin'
|
||
AWS_S3_ENDPOINT_URL = 'http://minio:9000'
|
||
```
|
||
|
||
### 方案 3:分布式文件系统
|
||
|
||
使用 Ceph、GlusterFS 等分布式文件系统。
|
||
|
||
## Redis 高可用
|
||
|
||
多实例部署时,Redis 是关键的共享存储:
|
||
|
||
### Redis Sentinel
|
||
|
||
```python
|
||
# 配置 Redis Sentinel
|
||
CACHES = {
|
||
'default': {
|
||
'BACKEND': 'django_redis.cache.RedisCache',
|
||
'LOCATION': 'redis://sentinel1:26379;redis://sentinel2:26379;redis://sentinel3:26379',
|
||
'OPTIONS': {
|
||
'CLIENT_CLASS': 'django_redis.client.SentinelClient',
|
||
'SENTINEL_KWARGS': {'sentinel_kwargs': {'password': 'sentinel_password'}},
|
||
'PASSWORD': 'redis_password',
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Redis Cluster
|
||
|
||
```python
|
||
# 配置 Redis Cluster
|
||
CACHES = {
|
||
'default': {
|
||
'BACKEND': 'django_redis.cache.RedisCache',
|
||
'LOCATION': [
|
||
'redis://node1:6379',
|
||
'redis://node2:6379',
|
||
'redis://node3:6379',
|
||
],
|
||
'OPTIONS': {
|
||
'CLIENT_CLASS': 'django_redis.client.ClusterClient',
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 数据库读写分离
|
||
|
||
### Django 配置
|
||
|
||
```python
|
||
# application/settings.py
|
||
DATABASES = {
|
||
'default': { # 主库(写)
|
||
'ENGINE': 'django.db.backends.mysql',
|
||
'HOST': 'master-db',
|
||
'PORT': 3306,
|
||
'NAME': 'djangoadmin',
|
||
'USER': 'root',
|
||
'PASSWORD': 'password',
|
||
},
|
||
'replica': { # 从库(读)
|
||
'ENGINE': 'django.db.backends.mysql',
|
||
'HOST': 'replica-db',
|
||
'PORT': 3306,
|
||
'NAME': 'djangoadmin',
|
||
'USER': 'root',
|
||
'PASSWORD': 'password',
|
||
},
|
||
}
|
||
|
||
# 数据库路由
|
||
DATABASE_ROUTERS = ['config.db_router.MasterSlaveRouter']
|
||
```
|
||
|
||
### 数据库路由
|
||
|
||
```python
|
||
# config/db_router.py
|
||
class MasterSlaveRouter:
|
||
"""主从数据库路由"""
|
||
|
||
def db_for_read(self, model, **hints):
|
||
return 'replica'
|
||
|
||
def db_for_write(self, model, **hints):
|
||
return 'default'
|
||
|
||
def allow_relation(self, obj1, obj2, **hints):
|
||
return True
|
||
|
||
def allow_migrate(self, db, app_label, model_name=None, **hints):
|
||
return db == 'default'
|
||
```
|
||
|
||
## Supervisor 多实例配置
|
||
|
||
```bash
|
||
; /etc/supervisor/conf.d/djangoadmin.conf
|
||
[program:djangoadmin-1]
|
||
command=/opt/venv/bin/gunicorn application.wsgi:application -c gunicorn.conf.py -b 127.0.0.1:8001
|
||
directory=/opt/project
|
||
user=www-data
|
||
autostart=true
|
||
autorestart=true
|
||
stdout_logfile=/var/log/djangoadmin/worker-1.log
|
||
|
||
[program:djangoadmin-2]
|
||
command=/opt/venv/bin/gunicorn application.wsgi:application -c gunicorn.conf.py -b 127.0.0.1:8002
|
||
directory=/opt/project
|
||
user=www-data
|
||
autostart=true
|
||
autorestart=true
|
||
stdout_logfile=/var/log/djangoadmin/worker-2.log
|
||
|
||
[program:djangoadmin-3]
|
||
command=/opt/venv/bin/gunicorn application.wsgi:application -c gunicorn.conf.py -b 127.0.0.1:8003
|
||
directory=/opt/project
|
||
user=www-data
|
||
autostart=true
|
||
autorestart=true
|
||
stdout_logfile=/var/log/djangoadmin/worker-3.log
|
||
|
||
[group:djangoadmin]
|
||
programs=djangoadmin-1,djangoadmin-2,djangoadmin-3
|
||
```
|
||
|
||
## 健康检查
|
||
|
||
Nginx 负载均衡需要健康检查来自动剔除故障节点:
|
||
|
||
```nginx
|
||
upstream djangoadmin {
|
||
server 127.0.0.1:8001 max_fails=3 fail_timeout=30s;
|
||
server 127.0.0.1:8002 max_fails=3 fail_timeout=30s;
|
||
server 127.0.0.1:8003 max_fails=3 fail_timeout=30s;
|
||
}
|
||
```
|
||
|
||
### 健康检查端点
|
||
|
||
```python
|
||
# application/health/views.py
|
||
def health_check(request):
|
||
"""健康检查端点"""
|
||
try:
|
||
with connection.cursor() as cursor:
|
||
cursor.execute("SELECT 1")
|
||
return JsonResponse({"status": "healthy"})
|
||
except Exception as e:
|
||
return JsonResponse({"status": "unhealthy", "error": str(e)}, status=503)
|
||
```
|
||
|
||
## 总结
|
||
|
||
多实例与负载均衡方案涵盖 Gunicorn 多 Worker、Nginx 负载均衡、JWT 无状态认证、文件存储共享、Redis 高可用、数据库读写分离、Supervisor 多实例配置、健康检查等方面。核心原则:无状态应用 + 共享存储 + 负载均衡。
|