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方案依然對其有效。

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配置裏面添加如下配置就可以了

location / {
            proxy_http_version 1.1; 
            proxy_set_header Upgrade $http_upgrade;                
            proxy_set_header Connection "upgrade";    
}

關注本公衆號的5000+筒靴們應該都知道,本號一直不遺餘力的輸出原創技術、職場心得,內容說不上什麼耳目一新、醍醐灌頂,但號主的技能點一直在進化,本次建立了一個[碼甲哥高質量交流羣],希望能和童鞋面對面成長(真誠臉圖片)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章