595 lines
12 KiB
Markdown
595 lines
12 KiB
Markdown
# SignalR 生产环境配置指南
|
||
|
||
**文档版本**: 1.0
|
||
**创建日期**: 2026-01-14
|
||
**适用环境**: 生产环境
|
||
|
||
---
|
||
|
||
## 一、Nginx 配置
|
||
|
||
### 1.1 WebSocket 支持配置
|
||
|
||
SignalR 使用 WebSocket 协议,需要在 Nginx 中配置 WebSocket 支持。
|
||
|
||
**配置文件**: `/etc/nginx/sites-available/xiangyi`
|
||
|
||
```nginx
|
||
# 上游服务器配置
|
||
upstream xiangyi_app_api {
|
||
server localhost:5000;
|
||
keepalive 32;
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
listen 443 ssl http2;
|
||
server_name app.zpc-xy.com;
|
||
|
||
# SSL 证书配置
|
||
ssl_certificate /etc/nginx/ssl/app.zpc-xy.com.crt;
|
||
ssl_certificate_key /etc/nginx/ssl/app.zpc-xy.com.key;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||
|
||
# 普通 HTTP API 请求
|
||
location /xyqj/api/ {
|
||
proxy_pass http://xiangyi_app_api/api/app/;
|
||
proxy_http_version 1.1;
|
||
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;
|
||
|
||
# 超时配置
|
||
proxy_connect_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_read_timeout 60s;
|
||
}
|
||
|
||
# SignalR WebSocket 配置(关键配置)
|
||
location /hubs/chat {
|
||
proxy_pass http://xiangyi_app_api/hubs/chat;
|
||
proxy_http_version 1.1;
|
||
|
||
# WebSocket 必需配置
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
|
||
# 基础代理头
|
||
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;
|
||
|
||
# 禁用缓存
|
||
proxy_cache_bypass $http_upgrade;
|
||
proxy_buffering off;
|
||
|
||
# 超时配置(WebSocket 需要长连接)
|
||
proxy_connect_timeout 7d;
|
||
proxy_send_timeout 7d;
|
||
proxy_read_timeout 7d;
|
||
|
||
# 心跳配置
|
||
proxy_socket_keepalive on;
|
||
}
|
||
|
||
# 静态文件
|
||
location /uploads/ {
|
||
alias /var/www/xiangyi/uploads/;
|
||
expires 30d;
|
||
add_header Cache-Control "public, immutable";
|
||
}
|
||
}
|
||
```
|
||
|
||
### 1.2 配置说明
|
||
|
||
**关键配置项**:
|
||
|
||
1. **proxy_http_version 1.1**
|
||
- WebSocket 需要 HTTP/1.1 协议
|
||
|
||
2. **Upgrade 和 Connection 头**
|
||
- 必须配置,用于协议升级
|
||
|
||
3. **超时时间**
|
||
- `proxy_read_timeout 7d` - 设置为 7 天,支持长连接
|
||
- 可根据实际需求调整
|
||
|
||
4. **proxy_buffering off**
|
||
- 禁用缓冲,确保消息实时传输
|
||
|
||
5. **proxy_socket_keepalive on**
|
||
- 启用 TCP keepalive,保持连接活跃
|
||
|
||
---
|
||
|
||
## 二、ASP.NET Core 配置
|
||
|
||
### 2.1 Program.cs 配置
|
||
|
||
```csharp
|
||
// SignalR 配置
|
||
builder.Services.AddSignalR(options =>
|
||
{
|
||
// 启用详细错误(生产环境建议关闭)
|
||
options.EnableDetailedErrors = false;
|
||
|
||
// 客户端超时时间(30秒)
|
||
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
||
|
||
// 心跳间隔(15秒)
|
||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||
|
||
// 最大消息大小(32KB)
|
||
options.MaximumReceiveMessageSize = 32 * 1024;
|
||
|
||
// 流式传输缓冲区大小
|
||
options.StreamBufferCapacity = 10;
|
||
});
|
||
|
||
// 如果使用 Redis 作为 SignalR 后端(多服务器部署)
|
||
builder.Services.AddSignalR()
|
||
.AddStackExchangeRedis(builder.Configuration.GetConnectionString("Redis"), options =>
|
||
{
|
||
options.Configuration.ChannelPrefix = "SignalR";
|
||
});
|
||
```
|
||
|
||
### 2.2 appsettings.Production.json
|
||
|
||
```json
|
||
{
|
||
"Logging": {
|
||
"LogLevel": {
|
||
"Default": "Information",
|
||
"Microsoft.AspNetCore.SignalR": "Warning",
|
||
"Microsoft.AspNetCore.Http.Connections": "Warning"
|
||
}
|
||
},
|
||
"ConnectionStrings": {
|
||
"Redis": "localhost:6379,password=your_redis_password"
|
||
},
|
||
"SignalR": {
|
||
"EnableDetailedErrors": false,
|
||
"ClientTimeoutInterval": 30,
|
||
"KeepAliveInterval": 15,
|
||
"MaximumReceiveMessageSize": 32768
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、Redis 配置(多服务器部署)
|
||
|
||
### 3.1 为什么需要 Redis?
|
||
|
||
当部署多个应用服务器实例时,SignalR 需要使用 Redis 作为后端存储,以实现:
|
||
- 跨服务器消息推送
|
||
- 连接状态共享
|
||
- 负载均衡支持
|
||
|
||
### 3.2 安装 Redis NuGet 包
|
||
|
||
```bash
|
||
cd server/src/XiangYi.AppApi
|
||
dotnet add package Microsoft.AspNetCore.SignalR.StackExchangeRedis
|
||
```
|
||
|
||
### 3.3 配置 Redis
|
||
|
||
```csharp
|
||
// Program.cs
|
||
builder.Services.AddSignalR()
|
||
.AddStackExchangeRedis(options =>
|
||
{
|
||
options.Configuration.EndPoints.Add("localhost", 6379);
|
||
options.Configuration.Password = "your_redis_password";
|
||
options.Configuration.ChannelPrefix = "SignalR";
|
||
options.Configuration.AbortOnConnectFail = false;
|
||
});
|
||
```
|
||
|
||
### 3.4 Redis 配置文件
|
||
|
||
```conf
|
||
# /etc/redis/redis.conf
|
||
|
||
# 绑定地址
|
||
bind 127.0.0.1
|
||
|
||
# 端口
|
||
port 6379
|
||
|
||
# 密码
|
||
requirepass your_redis_password
|
||
|
||
# 最大内存
|
||
maxmemory 256mb
|
||
|
||
# 内存淘汰策略
|
||
maxmemory-policy allkeys-lru
|
||
|
||
# 持久化
|
||
save 900 1
|
||
save 300 10
|
||
save 60 10000
|
||
```
|
||
|
||
---
|
||
|
||
## 四、防火墙配置
|
||
|
||
### 4.1 开放端口
|
||
|
||
```bash
|
||
# 开放 HTTP/HTTPS 端口
|
||
sudo ufw allow 80/tcp
|
||
sudo ufw allow 443/tcp
|
||
|
||
# 如果 Redis 需要外部访问
|
||
sudo ufw allow 6379/tcp
|
||
```
|
||
|
||
### 4.2 安全组规则(云服务器)
|
||
|
||
**入站规则**:
|
||
- HTTP: 80
|
||
- HTTPS: 443
|
||
- Redis: 6379(仅内网)
|
||
|
||
---
|
||
|
||
## 五、SSL/TLS 配置
|
||
|
||
### 5.1 获取 SSL 证书
|
||
|
||
**使用 Let's Encrypt**:
|
||
```bash
|
||
sudo apt-get install certbot python3-certbot-nginx
|
||
sudo certbot --nginx -d app.zpc-xy.com
|
||
```
|
||
|
||
### 5.2 自动续期
|
||
|
||
```bash
|
||
# 添加定时任务
|
||
sudo crontab -e
|
||
|
||
# 每天凌晨 2 点检查并续期
|
||
0 2 * * * certbot renew --quiet
|
||
```
|
||
|
||
### 5.3 强制 HTTPS
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name app.zpc-xy.com;
|
||
return 301 https://$server_name$request_uri;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、监控与日志
|
||
|
||
### 6.1 SignalR 连接监控
|
||
|
||
**创建监控端点**:
|
||
|
||
```csharp
|
||
// Controllers/MonitorController.cs
|
||
[ApiController]
|
||
[Route("api/monitor")]
|
||
public class MonitorController : ControllerBase
|
||
{
|
||
[HttpGet("signalr/stats")]
|
||
public IActionResult GetSignalRStats()
|
||
{
|
||
return Ok(new
|
||
{
|
||
OnlineUsers = ChatHub.GetOnlineUserCount(),
|
||
Timestamp = DateTime.Now
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.2 日志配置
|
||
|
||
**Serilog 配置**:
|
||
|
||
```json
|
||
{
|
||
"Serilog": {
|
||
"MinimumLevel": {
|
||
"Default": "Information",
|
||
"Override": {
|
||
"Microsoft.AspNetCore.SignalR": "Warning",
|
||
"Microsoft.AspNetCore.Http.Connections": "Warning"
|
||
}
|
||
},
|
||
"WriteTo": [
|
||
{
|
||
"Name": "File",
|
||
"Args": {
|
||
"path": "/var/log/xiangyi/signalr-.log",
|
||
"rollingInterval": "Day",
|
||
"retainedFileCountLimit": 30
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.3 性能监控
|
||
|
||
**关键指标**:
|
||
- 在线用户数
|
||
- 消息推送成功率
|
||
- 平均消息延迟
|
||
- 连接建立/断开频率
|
||
- 内存使用情况
|
||
|
||
**监控工具**:
|
||
- Application Insights
|
||
- Prometheus + Grafana
|
||
- ELK Stack
|
||
|
||
---
|
||
|
||
## 七、性能优化
|
||
|
||
### 7.1 连接池配置
|
||
|
||
```csharp
|
||
builder.Services.AddSignalR(options =>
|
||
{
|
||
// 限制并发连接数
|
||
options.MaximumParallelInvocationsPerClient = 1;
|
||
|
||
// 启用消息压缩
|
||
options.EnableDetailedErrors = false;
|
||
});
|
||
```
|
||
|
||
### 7.2 消息压缩
|
||
|
||
```csharp
|
||
builder.Services.AddSignalR()
|
||
.AddMessagePackProtocol(); // 使用 MessagePack 协议(更高效)
|
||
```
|
||
|
||
### 7.3 数据库优化
|
||
|
||
```sql
|
||
-- 为聊天表添加索引
|
||
CREATE INDEX IX_ChatMessage_SessionId_CreateTime
|
||
ON Chat_Message(SessionId, CreateTime DESC);
|
||
|
||
CREATE INDEX IX_ChatMessage_ReceiverId_IsRead
|
||
ON Chat_Message(ReceiverId, IsRead);
|
||
|
||
CREATE INDEX IX_ChatSession_User1Id_User2Id
|
||
ON Chat_Session(User1Id, User2Id);
|
||
```
|
||
|
||
---
|
||
|
||
## 八、故障排查
|
||
|
||
### 8.1 连接失败
|
||
|
||
**检查清单**:
|
||
1. Nginx 配置是否正确
|
||
2. WebSocket 是否被防火墙阻止
|
||
3. SSL 证书是否有效
|
||
4. 后端服务是否运行
|
||
|
||
**诊断命令**:
|
||
```bash
|
||
# 检查 Nginx 配置
|
||
sudo nginx -t
|
||
|
||
# 查看 Nginx 日志
|
||
sudo tail -f /var/log/nginx/error.log
|
||
|
||
# 检查端口监听
|
||
sudo netstat -tlnp | grep 5000
|
||
|
||
# 测试 WebSocket 连接
|
||
wscat -c wss://app.zpc-xy.com/hubs/chat
|
||
```
|
||
|
||
### 8.2 消息丢失
|
||
|
||
**可能原因**:
|
||
- Redis 连接断开
|
||
- 消息队列满
|
||
- 客户端未正确监听事件
|
||
|
||
**解决方案**:
|
||
1. 检查 Redis 连接状态
|
||
2. 增加消息队列大小
|
||
3. 实现消息持久化和重试机制
|
||
|
||
### 8.3 性能问题
|
||
|
||
**优化建议**:
|
||
1. 启用 Redis 集群
|
||
2. 使用 MessagePack 协议
|
||
3. 实现消息批量推送
|
||
4. 优化数据库查询
|
||
5. 使用 CDN 加速静态资源
|
||
|
||
---
|
||
|
||
## 九、安全配置
|
||
|
||
### 9.1 认证授权
|
||
|
||
```csharp
|
||
// ChatHub.cs
|
||
[Authorize] // 必须登录才能连接
|
||
public class ChatHub : Hub
|
||
{
|
||
// 验证用户权限
|
||
public override async Task OnConnectedAsync()
|
||
{
|
||
var userId = GetCurrentUserId();
|
||
if (userId <= 0)
|
||
{
|
||
Context.Abort();
|
||
return;
|
||
}
|
||
await base.OnConnectedAsync();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 9.2 速率限制
|
||
|
||
```csharp
|
||
// 限制消息发送频率
|
||
public class RateLimitAttribute : ActionFilterAttribute
|
||
{
|
||
private static readonly ConcurrentDictionary<long, DateTime> LastMessageTime = new();
|
||
|
||
public override void OnActionExecuting(ActionExecutingContext context)
|
||
{
|
||
var userId = GetUserId(context);
|
||
var now = DateTime.Now;
|
||
|
||
if (LastMessageTime.TryGetValue(userId, out var lastTime))
|
||
{
|
||
if ((now - lastTime).TotalSeconds < 1) // 1秒内只能发送1条
|
||
{
|
||
context.Result = new StatusCodeResult(429); // Too Many Requests
|
||
return;
|
||
}
|
||
}
|
||
|
||
LastMessageTime[userId] = now;
|
||
base.OnActionExecuting(context);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 9.3 消息过滤
|
||
|
||
```csharp
|
||
// 敏感词过滤
|
||
public async Task<string> FilterSensitiveWords(string content)
|
||
{
|
||
// 实现敏感词过滤逻辑
|
||
return content;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 十、部署检查清单
|
||
|
||
### 10.1 部署前检查
|
||
|
||
- [ ] Nginx 配置已更新
|
||
- [ ] SSL 证书已配置
|
||
- [ ] Redis 已安装并配置
|
||
- [ ] 防火墙规则已配置
|
||
- [ ] 日志目录已创建
|
||
- [ ] 监控已配置
|
||
|
||
### 10.2 部署后验证
|
||
|
||
- [ ] WebSocket 连接成功
|
||
- [ ] 消息实时推送正常
|
||
- [ ] 断线重连正常
|
||
- [ ] 多设备同时在线正常
|
||
- [ ] 性能指标正常
|
||
- [ ] 日志记录正常
|
||
|
||
### 10.3 压力测试
|
||
|
||
```bash
|
||
# 使用 Artillery 进行压力测试
|
||
npm install -g artillery
|
||
|
||
# 创建测试脚本 signalr-test.yml
|
||
artillery run signalr-test.yml
|
||
```
|
||
|
||
**测试脚本示例**:
|
||
```yaml
|
||
config:
|
||
target: "wss://app.zpc-xy.com"
|
||
phases:
|
||
- duration: 60
|
||
arrivalRate: 10
|
||
engines:
|
||
ws:
|
||
timeout: 30000
|
||
|
||
scenarios:
|
||
- name: "SignalR Connection Test"
|
||
engine: ws
|
||
flow:
|
||
- connect:
|
||
url: "/hubs/chat?access_token={{token}}"
|
||
- think: 5
|
||
- send: '{"type":1,"target":"SendMessage","arguments":["Hello"]}'
|
||
- think: 10
|
||
```
|
||
|
||
---
|
||
|
||
## 十一、回滚方案
|
||
|
||
### 11.1 快速回滚
|
||
|
||
如果部署后出现问题,可以快速回滚:
|
||
|
||
```bash
|
||
# 回滚到上一个版本
|
||
cd /var/www/xiangyi/app-api
|
||
git checkout previous-version
|
||
dotnet publish -c Release -o /var/www/xiangyi/app-api/publish
|
||
sudo systemctl restart xiangyi-app-api
|
||
```
|
||
|
||
### 11.2 降级方案
|
||
|
||
如果 SignalR 出现问题,可以临时禁用实时推送:
|
||
|
||
```csharp
|
||
// Program.cs
|
||
if (!builder.Configuration.GetValue<bool>("SignalR:Enabled"))
|
||
{
|
||
// 不注册 SignalR
|
||
}
|
||
else
|
||
{
|
||
builder.Services.AddSignalR();
|
||
app.MapHub<ChatHub>("/hubs/chat");
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 十二、总结
|
||
|
||
SignalR 生产环境配置要点:
|
||
|
||
1. **Nginx 配置** - 正确配置 WebSocket 支持
|
||
2. **SSL/TLS** - 使用 wss:// 协议
|
||
3. **Redis** - 多服务器部署必需
|
||
4. **监控** - 实时监控连接状态和性能
|
||
5. **安全** - 认证、授权、速率限制
|
||
6. **优化** - 连接池、消息压缩、数据库索引
|
||
|
||
完成以上配置后,SignalR 即可在生产环境稳定运行。
|