SignalR 从开发到生产部署避坑指南

前天倒腾了一份[SignalR在react/go技术栈的实践], 步骤和思路大部分是外围框架的应用, 今天趁热打铁, 给一个我总结的SignalR避坑指南。

1.SignalR 默认协商

不管是.NET客户端还是JavaScript客户端,构建连接时都存在一个默认配置:SkipNegotiation=fasle,负负得正就等于要求协商,这个默认配置的完整含义是 建立SignalR连接时,客户端要求协商传输方式

对应产生下图:

小技巧:如果你确定你的网络环境能稳定的走websocket传输, 为了快速建立实时通信,可跳过协商请求(设置SkipNegotiation=true), 毕竟每次刷新页面,react组价都会重新加载,重新协商再传输 费时费力。

const connection = new HubConnectionBuilder()
.withUrl(process.env.REACT_APP_APIBASEURL+"realtime", {
skipNegotiation: true,
transport: HttpTransportType.WebSockets
})
.withAutomaticReconnect()
.withHubProtocol(new JsonHubProtocol())
.configureLogging(LogLevel.Information)
.build();

注意: SkipNegotiation=true,仅限于客户端的传输方式指定为 websocket, 其他方式均会报错。

2.SignalR 传输协商是fetch请求

跟ajax一样,fetch请求也是浏览器脚本的一种,所以很明显也会涉及跨域,标准的CORS方案依然对其有效。

http://localhost:9598/realtime/negotiate?negotiateVersion=1
Post请求
有自定义的请求头 X-Requested-With, X-Signalr-User-Agent

很明显,这又会触发预检Option请求

故你还需要在使用 CORS Middleware时允许这几个自定义请求头。

// 下面是Go github.com/rs/cors package 支持CORS的代码
c := cors.New(cors.Options{
// AllowedOrigins: []string{"http://localhost:3000","http://rosenbridge.17usoft.com"},
AllowOriginFunc: func(origin string) bool {
return true
},
AllowedMethods: []string{"POST", "GET", "OPTIONS", "PUT", "DELETE"}, // 下面要加上signalr传输协商要用到的自定义请求头
AllowedHeaders: []string{"Content-Type", "x-requested-with", "x-signalr-user-agent"},
AllowCredentials: true,
Debug: cfg.Log.Debug,
})

3. websocket也有同源限制

ws://localhost:9598/realtime?id=aoSD_WZhqbRfPyXVTYsHig==

WebSocket也有同源限制,但是标准的CORS对其无效,因为CORS解决是HTTP脚本请求的跨域问题,WebSocket说到底不算http协议。

浏览器依旧会为我们携带Origin标头,所以服务端需要验证这些标头,确保只允许来自预期来源的WebSocket。

// 以下是.NET Core 针对websocket同源限制做出的跨域策略
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);

btw, 我使用的GO SignalR库不支持WebSocket跨域, 我提了一个PR, 已经成功合并,兴奋,这是我首次向开源项目提PR且获得通过的项目。

实际传输效果:

4. 部署生产后,需要nginx支持

按照默认配置,一般会先协商,再使用websocket传输。

部署到生产之后,协商后优先使用WebSocket模式, 但是传输失败了, 自动降级为服务器发送事件SSE模式,传输成功。

浏览器开发者工具看不出啥端倪, 使用Fiddler抓包发现 400 状态码

网上搜索了一下,nginx默认丢弃了客户端 websocket请求的标记头, 导致nginx==> upstreamServer7层无法建立websocket连接
在nginx配置里面添加如下配置就可以了

location / { // 放在/根目录下,不会对正常的http1.1请求造成影响的,因为一般的后端的Server对于http请求都会忽略这两个新增的header
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
  • proxy_set_header Connection 代理转发的wss协议,所以http请求头的Connection设置为Upgrade,要求升级。
  • proxy_set_header Upgrade 代理转发时将http请求头的Upgrade设置为原来http请求的请求头,wss协议的请求头为websocket

关注本公众号的5000+筒靴们应该都知道,本号一直不遗余力的输出原创技术、职场心得,内容说不上什么耳目一新、醍醐灌顶,但号主的技能点一直在进化,本次建立了一个[码甲哥高质量交流群],希望能和童鞋面对面成长(真诚脸图片)。

posted @   码甲哥不卷  阅读(1779)  评论(2编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示

目录导航