xiangyixiangqin/server/SignalR生产环境配置.md
2026-01-14 20:23:13 +08:00

595 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 即可在生产环境稳定运行。