SignalR在React/Go技术栈的实践转载请联系精益码农公众号。一、背景有一个前后端分离的运维开发web平台。后端每5分钟同步一次数据。现在需要将最新的同步时间推送到web前端。说到【webserverpush】,立马想到了SignalR。(脑子里一直有一个技术体系,但是一直没有实践过)SignalR是微软推出的一个实时通信标准框架。它内部封装了websocket,服务端发送事件,longPolling可以看做是实时通信的杀手锏,传送门。实际编码是react写SignalRclient,golang写SignalRserver,有对应的轮子可以瞎猜。2.撸起袖子干起来果然,signalr的作者DavidFowler已经实现了node和go版本。这位老哥是.NET技术栈的高手:但是他的仓库已经很久没有更新了,某德国大佬在此基础上开了一个新的github仓库[1]继续支持。SignalR的基本交互原理:(1)signalR提供了一组用于创建从服务器到客户端的远程过程调用(RPC)的API。这种调用的具体体现是:从服务端代码上的.NET代码调用客户端上的javascript。(2)signalr提供了管理实例、连接、断开和组控制的API。这里最关键的概念就是hubHub,其实就是RPC领域中常说的客户端代理。服务器在baseUrl上建立signalr的监听地址;客户端连接并注册接收事件;服务器在适当的时候通过hubServer向HubClients发送数据。goserver(1)添加golangpgk:gogetgithub.com/philippseith/signalr(2)定义clienthubhub,这里有几个实现HubInterface接口的方法,也可以为hub添加一些自定义方法。packageservicesimport("github.com/philippseith/signalr"log"github.com/sirupsen/logrus""time")typeAppHubstruct{signalr.Hub}func(h*AppHub)OnConnected(connectionIDstring){//fmt.Printf("%sconnected\n",connectionID)log.Infoln(connectionID,"connected\n")}func(h*AppHub)OnDisconnected(connectionIDstring){log.Infoln(connectionID,"disconnected\n")}//客户端调用Function,此示例不使用func(h*AppHub)Send(messagestring){h.Clients().All().Send("receive",time.Now().Format("2006/01/0215:04:05"))}(3)初始化集线器并在特定地址侦听信号器请求。这个库将signalr监听服务抽象成一个独立的hubServershub:=services.AppHub{}sHubSrv,err:=signalr.NewServer(context.TODO(),signalr.UseHub(&shub),//这是一个单例的hubsignalr.KeepAliveInterval(2*time.Second),signalr.Logger(kitlog.NewLogfmtLogger(os.Stderr),true))sHubSrv.MapHTTP(mux,"/realtime")(4)在合适的业务使用sHubServer向web客户端推送数据代码位置。ifclis:=s.sHubServer.HubClients();clis!=nil{c:=clis.All()ifc!=nil{c.Send("receive",ts.Format("2006/01/0215:04:05"))}}注意:上面的receive方法是reactclient后面需要监听的JavaScript事件名。react客户端的前端鸡按照官方的例子琢磨了好几天。(1)添加@microsoft/signalr包(2)在组件挂载事件componentDidMount中初始化signalr连接,其实就是建立HubConnection到服务器baseUrl,注册接收事件,等待服务器推送。importReactfrom'react';import{JsonHubProtocol,HubConnectionState,HubConnectionBuilder,HttpTransportType,LogLevel,}from'@microsoft/signalr';classClockextendsReact.Component{constructor(props){super(props);this.state={message:'',hubConnection:null,};}componentDidMount(){constconnection=newHubConnectionBuilder().withUrl(process.env.REACT_APP_APIBASEURL+"realtime",{}).withAutomaticReconnect().withHubProtocol(newJsonHubProtocol()).configureLogging(LogLevel.Information).build();//Note:tokeeptheconnectionopentheserverTimeoutshouldbe//largerthantheKeepAlivevaluethatissetontheserver//keepAliveIntervalInMillisecondsdefaultis15000andweareusingdefault//serverTimeoutInMillisecondsdefaultis30000andweareusing60000setbelowconnection.serverTimeoutInMilliseconds=60000;//re-establishtheconnectionifconnectiondroppedconnection.onclose(error=>{console.assert(connection.state===HubConnectionState.Disconnected);console.log('Connectionclosedduetoerror.Tryrefreshingthispagetorestarttheconnection',error);});connection.onreconnecting(error=>{console.assert(connection.state===HubConnectionState.Reconnecting));console.log('Connectionlostduetoerror.Reconnecting.',error);});connection.onreconnected(connectionId=>{console.assert(connection.state===HubConnectionState.Connected);console.log('Connectionreestablished.ConnectedwithconnectionId',connectionId);});this.setState({hubConnection:connection})这个。startSignalRConnection(connection).then(()=>{if(connection.state===HubConnectionState.Connected){connection.invoke('RequestSyncTime').then(val=>{console.log("Signalrgetdatafirsttime:",val);this.setState({message:val})})}});connection.on('receive',res=>{console.log("SignalRgethotres:",res)this.setState({message:res});});}startSignalRConnection=asyncconnection=>{try{awaitconnection.start();console.assert(connection.state===HubConnectionState.Connected);console.log('SignalRconconnectionestablished');}catch(err){console.assert(connection.state===HubConnectionState.Disconnected);console.error('SignalRConnectionError:',err);setTimeout(()=>this.startSignalRConnection(connection),5000);}};render(){return(最新同步完成时间:{this.state.message}