SingnalR 從開發到生產部署閉坑指南
前天倒騰了一份[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方案依然對其有效。
//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{"//localhost:3000","//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("//client.com");
webSocketOptions.AllowedOrigins.Add("//www.client.com");
app.UseWebSockets(webSocketOptions);
btw, 我使用的GO SignalR庫不支援WebSocket跨域, 我提了一個PR, 已經成功合併,興奮,這是我首次向開源項目提MR且獲得通過的項目。
不僅signalr協商請求需要 CORS, 真正傳輸的websocket協議也有跨域一說,我使用了一個庫,沒有做到cors,我提了一個mr,成功合併。
4. 部署生產後,需要nginx支援
按照默認配置,一般會先協商,再使用websocket傳輸。
部署到生產之後,協商後優先使用WebSocket模式
, 但是傳輸失敗了, 自動降級為伺服器發送事件SSE
模式,傳輸成功。
瀏覽器開發者工具看不出啥端倪, 使用Fiddler抓包發現 400 狀態碼
網上搜索了一下,可能是生產的nginx不識別websocket標頭。
在nginx配置裡面添加如下配置就可以了
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
關注本公眾號的5000+筒靴們應該都知道,本號一直不遺餘力的輸出原創技術、職場心得,內容說不上什麼耳目一新、醍醐灌頂,但號主的技能點一直在進化,本次建立了一個[碼甲哥高品質交流群],希望能和童鞋面對面成長(真誠臉圖片)。