SignalR 在 React/Go 技术栈的实践

2025-05-29 0 79

SignalR 在 React/Go 技术栈的实践

一.背景

有个前后端分离的运维开发web平台, 后端会间隔5分钟同步一次数据,现在需要将最新一次同步的时间推送到web前端。

说到[web服务端推送],立马想到SignalR,(我头脑中一直有技术体系, 但一直没实践过。)

SignalR是微软推出的实时通信标准框架,内部封装了 websocket、服务端发送事件、长轮询, 可以算是实时通信的大杀器,传送门。

实际编码就是react写SignalR客户端,golang写SignalR服务端,盲猜有对应的轮子。

二.撸起袖子干

果然, signalr的作者David Fowler实现了node、go版本, 这位老哥是.NET技术栈如雷贯耳的大牛:

SignalR 在 React/Go 技术栈的实践

但是他的仓库很久不更了,某德国大佬在此基础上开了新github仓库[1]继续支持。

SignalR的基本交互原理:

(1) signalR提供了一组API, 用于创建从服务端到客户端的远程过程调用(RPC),这个调用的具体体现是 :从服务端.NET 代码调用位于客户端的javascript 代码。

(2) signalr提供了管理实例、连接、失连, 分组管控的API。

SignalR 在 React/Go 技术栈的实践

这里面最关键的一个概念是集线器Hub,其实也就是RPC领域常说的客户端代理。

服务端在baseUrl上建立signalr的监听地址;

客户端连接并注册receive事件;

服务端在适当时候通过hubServer向HubClients发送数据。

go服务端

(1) 添加golang pgk:go get github.com/philippseith/signalr

(2) 定义客户端集线器hub,这里要实现HubInterface接口的几个方法, 你还可以为集线器添加一些自定义方法。

  1. packageservices
  2. import(
  3. "github.com/philippseith/signalr"
  4. log"github.com/sirupsen/logrus"
  5. "time"
  6. )
  7. typeAppHubstruct{
  8. signalr.Hub
  9. }
  10. func(h*AppHub)OnConnected(connectionIDstring){
  11. //fmt.Printf("%sconnected\\n",connectionID)
  12. log.Infoln(connectionID,"connected\\n")
  13. }
  14. func(h*AppHub)OnDisconnected(connectionIDstring){
  15. log.Infoln(connectionID,"disconnected\\n")
  16. }
  17. //客户端调用的函数,本例不用
  18. func(h*AppHub)Send(messagestring){
  19. h.Clients().All().Send("receive",time.Now().Format("2006/01/0215:04:05"))
  20. }

(3) 初始化集线器, 并在特定地址监听signalr请求。

这个库将signalr监听服务抽象为独立的hubServer

  1. shub:=services.AppHub{}
  2. sHubSrv,err:=signalr.NewServer(context.TODO(),
  3. signalr.UseHub(&shub),//这是单例hub
  4. signalr.KeepAliveInterval(2*time.Second),
  5. signalr.Logger(kitlog.NewLogfmtLogger(os.Stderr),true))
  6. sHubSrv.MapHTTP(mux,"/realtime")

(4) 利用sHubServer在合适业务代码位置向web客户端推送数据。

  1. ifclis:=s.sHubServer.HubClients();clis!=nil{
  2. c:=clis.All()
  3. ifc!=nil{
  4. c.Send("receive",ts.Format("2006/01/0215:04:05"))
  5. }
  6. }

注意:上面的receive方法是后面react客户端需要监听的JavaScript事件名。

react客户端

前端菜鸡,跟着官方示例琢磨了好几天。

(1) 添加@microsoft/signalr 包

(2) 在组件挂载事件componentDidMount初始化signalr连接

实际也就是向服务端baseUrl建立HubConnection,注册receive事件,等待服务端推送。

  1. importReactfrom'react';
  2. import{
  3. JsonHubProtocol,
  4. HubConnectionState,
  5. HubConnectionBuilder,
  6. HttpTransportType,
  7. LogLevel,
  8. }from'@microsoft/signalr';
  9. classClockextendsReact.Component{
  10. constructor(props){
  11. super(props);
  12. this.state={
  13. message:'',
  14. hubConnection:null,
  15. };
  16. }
  17. componentDidMount(){
  18. constconnection=newHubConnectionBuilder()
  19. .withUrl(process.env.REACT_APP_APIBASEURL+"realtime",{
  20. })
  21. .withAutomaticReconnect()
  22. .withHubProtocol(newJsonHubProtocol())
  23. .configureLogging(LogLevel.Information)
  24. .build();
  25. //Note:tokeeptheconnectionopentheserverTimeoutshouldbe
  26. //largerthantheKeepAlivevaluethatissetontheserver
  27. //keepAliveIntervalInMillisecondsdefaultis15000andweareusingdefault
  28. //serverTimeoutInMillisecondsdefaultis30000andweareusing60000setbelow
  29. connection.serverTimeoutInMilliseconds=60000;
  30. //re-establishtheconnectionifconnectiondropped
  31. connection.onclose(error=>{
  32. console.assert(connection.state===HubConnectionState.Disconnected);
  33. console.log('Connectionclosedduetoerror.Tryrefreshingthispagetorestarttheconnection',error);
  34. });
  35. connection.onreconnecting(error=>{
  36. console.assert(connection.state===HubConnectionState.Reconnecting);
  37. console.log('Connectionlostduetoerror.Reconnecting.',error);
  38. });
  39. connection.onreconnected(connectionId=>{
  40. console.assert(connection.state===HubConnectionState.Connected);
  41. console.log('Connectionreestablished.ConnectedwithconnectionId',connectionId);
  42. });
  43. this.setState({hubConnection:connection})
  44. this.startSignalRConnection(connection).then(()=>{
  45. if(connection.state===HubConnectionState.Connected){
  46. connection.invoke('RequestSyncTime').then(val=>{
  47. console.log("Signalrgetdatafirsttime:",val);
  48. this.setState({message:val})
  49. })
  50. }
  51. });
  52. connection.on('receive',res=>{
  53. console.log("SignalRgethotres:",res)
  54. this.setState({
  55. message:res
  56. });
  57. });
  58. }
  59. startSignalRConnection=asyncconnection=>{
  60. try{
  61. awaitconnection.start();
  62. console.assert(connection.state===HubConnectionState.Connected);
  63. console.log('SignalRconnectionestablished');
  64. }catch(err){
  65. console.assert(connection.state===HubConnectionState.Disconnected);
  66. console.error('SignalRConnectionError:',err);
  67. setTimeout(()=>this.startSignalRConnection(connection),5000);
  68. }
  69. };
  70. render(){
  71. return(
  72. <divstyle={{width:'300px',float:'left',marginLeft:'10px'}}>
  73. <h4>最新同步完成时间:{this.state.message}</h4>
  74. </div>
  75. );
  76. }
  77. }
  78. exportdefaultClock;

(3) 将该react组件插入到web前端页面

三.效果分析

最后的效果如图:

SignalR 在 React/Go 技术栈的实践

效果分析:

(1) web客户端与服务器协商 传输方式http://localhost:9598/realtime/negotiate?negotiateVersion=1,

返回可用的传输方式和连接标识ConnectionId。

  1. {
  2. "connectionId":"hkSNQT-pGpZ9E6tuMY9rRw==",
  3. "availableTransports":[{
  4. "transport":"WebSockets",
  5. "transferFormats":["Text","Binary"]
  6. },{
  7. "transport":"ServerSentEvents",
  8. "transferFormats":["Text"]
  9. }]
  10. }

(2) web客户端利用上面的ConnectionId向特定的服务器地址/realtime连接,建立传输通道,默认优先websocket。

SignalR 在 React/Go 技术栈的实践

以上网络交互,大部分会通过SignalR框架自动完成。

源码:Github Demo[2]

引用链接

[1] Github仓库: https://github.com/philippseith/signalr

[2] Github Demo: https://github.com/zaozaoniao/SignalR-apply-to-react-and-golang

原文链接:https://mp.weixin.qq.com/s/O8ysCDsIG8mjarv_A31BIw

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 SignalR 在 React/Go 技术栈的实践 https://www.kuaiidc.com/90122.html

相关文章

发表评论
暂无评论