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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM