SignalR 在 React/Go 技术栈的实践

本文转载自微信公众号「精益码农」,技术作者有态度的实践马甲 。转载本文请联系精益码农公众号。技术

一.背景

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

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

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

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

二.撸起袖子干

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

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

SignalR的基本交互原理:

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

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

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

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

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

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

go服务端

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

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

package services import (  "github.com/philippseith/signalr"  log "github.com/sirupsen/logrus"  "time" ) type AppHub struct{    signalr.Hub } func (h *AppHub) OnConnected(connectionID string) {   // fmt.Printf("%s connected\n", connectionID)  log.Infoln(connectionID," connected\n" ) } func (h *AppHub) OnDisconnected(connectionID string) {   log.Infoln(connectionID," disconnected\n") } // 客户端调用的函数, 本例不用 func (h *AppHub) Send(message string) {   h.Clients().All().Send("receive", time.Now().Format("2006/01/02 15:04:05") ) } 

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

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

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

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

if clis:= s.sHubServer.HubClients(); clis!= nil {      c:= clis.All()     if  c!= nil {       c.Send("receive",ts.Format("2006/01/02 15:04:05"))     }    } 

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

react客户端

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

(1) 添加@microsoft/signalr 包

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

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

import React from react; import {    JsonHubProtocol,   HubConnectionState,   HubConnectionBuilder,   HttpTransportType,   LogLevel, } from @microsoft/signalr; class Clock extends React.Component {      constructor(props) {        super(props);       this.state = {          message:,         hubConnection: null,       };     }     componentDidMount() {        const connection = new HubConnectionBuilder()         .withUrl(process.env.REACT_APP_APIBASEURL+"realtime", {          })         .withAutomaticReconnect()         .withHubProtocol(new JsonHubProtocol())         .configureLogging(LogLevel.Information)         .build();     // Note: to keep the connection open the serverTimeout should be     // larger than the KeepAlive value that is set on the server     // keepAliveIntervalInMilliseconds default is 15000 and we are using default     // serverTimeoutInMilliseconds default is 30000 and we are using 60000 set below         connection.serverTimeoutInMilliseconds = 60000;     // re-establish the connection if connection dropped         connection.onclose(error => {              console.assert(connection.state === HubConnectionState.Disconnected);             console.log(Connection closed due to error. Try refreshing this page to restart the connection, error);         });         connection.onreconnecting(error => {              console.assert(connection.state === HubConnectionState.Reconnecting);             console.log(Connection lost due to error. Reconnecting., error);         });         connection.onreconnected(connectionId => {              console.assert(connection.state === HubConnectionState.Connected);             console.log(Connection reestablished. Connected with connectionId, connectionId);         });         this.setState({  hubConnection: connection})         this.startSignalRConnection(connection).then(()=> {                if(connection.state === HubConnectionState.Connected) {                  connection.invoke(RequestSyncTime).then(val => {                    console.log("Signalr get data first time:",val);                   this.setState({  message:val })                 })               }         }) ;         connection.on(receive, res => {            console.log("SignalR get hot res:", res)             this.setState({                message:res             });         });     }     startSignalRConnection = async connection => {        try {            await connection.start();           console.assert(connection.state === HubConnectionState.Connected);           console.log(SignalR connection established);       } catch (err) {            console.assert(connection.state === HubConnectionState.Disconnected);           console.error(SignalR Connection Error: , err);           setTimeout(() => this.startSignalRConnection(connection), 5000);       }     };     render() {        return (         <div style={ { width: 300px,float:left,marginLeft:10px}} >           <h4>最新同步完成时间: { this.state.message}  </h4>         </div>       );     }   } export  default  Clock; 

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

三.效果分析

最后的效果如图:

效果分析:

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

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

{      "connectionId": "hkSNQT-pGpZ9E6tuMY9rRw==",     "availableTransports": [{          "transport": "WebSockets",         "transferFormats": ["Text", "Binary"]     }, {          "transport": "ServerSentEvents",         "transferFormats": ["Text"]     }] } 

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

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

源码:Github Demo[2]

引用链接

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

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

滇ICP备2023000592号-31