SignalR 在React/GO技术栈的生产应用
哼哧哼哧半年,优化改进了一个运维开发web平台。
本文记录SignalR在react/golang 技术栈的生产小实践。
1. 背景
有个前后端分离的运维开发web平台, 后端会间隔5分钟同步一次数据,现在需要将最新一次同步的时间推送到web前端。
说到[web服务端推送],立马想到SignalR,(我头脑中一直有技术体系, 但一直没实践过。)
signalr是微软推出的实时通信标准框架,内部封装了 websocket、服务端发送事件、长轮询, 可以算是实时通信的大杀器,传送门。
实际编码就是react写signalr客户端,golang写signalr服务端,盲猜有对应的轮子。
2. 撸起袖子干
果然, signalr的作者David Fowler实现了node、go版本, 这位老哥是.NET技术栈如雷贯耳的大牛:

但是他的仓库很久不更了,某德国大佬在此基础上开了新github仓库继续支持。
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,这里要实现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 在React/GO技术栈的生产应用的更多相关文章
- webpack+babel+react+antd技术栈的基础配置
webpack+babel+react+antd技术栈的基础配置 前段时间使用webpack+babel+react+antd做了一套后台管理系统,刚开始被一大堆的新知识压的喘不过气来,压力挺大的.还 ...
- 用react系列技术栈实现的demo整合系统
引子 学生时代为了掌握某个知识点会不断地做习题,做总结,步入岗位之后何尝不是一样呢?做业务就如同做习题,如果‘课后’适当地进行总结,必然更快地提升自己的水平. 由于公司采用的react+node的技术 ...
- 一个基于React整套技术栈+Node.js的前端页面制作工具
pagemaker是一个前端页面制作工具,方便产品,运营和视觉的同学迅速开发简单的前端页面,从而可以解放前端同学的工作量.此项目创意来自网易乐得内部项目nfop中的pagemaker项目.原来项目的前 ...
- 重谈react优势——react技术栈回顾
react刚刚推出的时候,讲react优势搜索结果是几十页. 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文. 今天,再谈一遍react优 ...
- react技术栈实践(2)
本文来自网易云社区 作者:汪洋 这时候还没完,又有两个问题引出来了. 按照上面的配置,第三方库 antd 竟然也被编译了,导致样式失败. react中,一旦包裹了子组件,子组件没办法直接使用 styl ...
- 从开发一款基于Vue技术栈的全栈热重载生产环境脚手架,我学到了什么
浏览文章前 这一期,我分享给大家三点看源码的小技巧,这也是从别的大佬那总结的. 被反复使用的代码 这样的代码是一个软件的重点函数,一个大神的写法有很多精华值得学习. 穿越时间的代码 如果一段代码10年 ...
- 通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? .Net Web开发技术栈
通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念 ...
- 一个人的 ClojureScript 技术栈
作者:题叶链接:https://zhuanlan.zhihu.com/p/24425284来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.今天(昨天)分享完关于 Cloj ...
- .Net Web开发技术栈
有很多朋友有的因为兴趣,有的因为生计而走向了.Net中,有很多朋友想学,但是又不知道怎么学,学什么,怎么系统的学,为此我以我微薄之力总结归纳写了一篇.Net web开发技术栈,以此帮助那些想学,却不知 ...
随机推荐
- BUUCTF-[CISCN2019 华北赛区 Day1 Web5]CyberPunk
BUUCTF-[CISCN2019 华北赛区 Day1 Web5]CyberPunk 看题 看源码有提示?file=? 文件包含漏洞,可以利用这个漏洞读取源码. 分析 index.php?file=p ...
- GUI常用监听事件
概念 对鼠标.键盘等一系列事件做出相应的反馈 事件监听 //创建监听事件 public class Demo { public static void main(String[] args) { Fr ...
- 集合框架2- ArrayList
其实 Java 集合框架也叫做容器,主要由两大接口派生而来,一个是 collection,主要存放对象的集合.另外一个是Map, 存储着键值对(两个对象)的映射表. 下面就来说说 List接口,Lis ...
- SpringBoot 属性配置文件数据注入配置和yml与properties区别
前言 我们知道SpringBoot 通过配置类来解放一堆的xml文件配置,通属性配置文件,来进行,系统全局属性配置,这样极大的简化了我们开发过程,java web 也可以甜甜的从此 快速配置 Spri ...
- Django的form组件基本使用——简单校验
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = ...
- sed中传递变量进行替换
sed命令中传递变量 例如:修改配置文件某一个变量的值 配置文件如下: toney@ubantu:/mnt/hgfs/em嵌入式学习记录/shell/shell脚本常见用法$ cat common_u ...
- JS002. map( ) 和 filter( ) 的区别和实际应用场景(递归函数、深度优先搜索DFS)
在开发过程中难免会碰到省市区级联的操作,一般后端人员是不愿意将中文储存在数据库的. 由于应用页面较多,我们在通过区域Code写查字典函数时应该注意函数的 时间复杂度 / 空间复杂度. 如果用三层for ...
- aes加解密前后端-前端
一.ajax请求前端 f12请求和响应参数效果: 1.在前端封装ajax的公共Util的js中,封装ajax请求的地方,在beforeSend方法和成功之后的回调函数success方法中: var p ...
- 【第十二篇】- Git 服务器搭建之Spring Cloud直播商城 b2b2c电子商务技术总结
Git 服务器搭建 上一章节中我们远程仓库使用了 Github,Github 公开的项目是免费的,2019 年开始 Github 私有存储库也可以无限制使用. 这当然我们也可以自己搭建一台 Git 服 ...
- 尚硅谷Java高级笔记
尚硅谷Java高级笔记 idea的使用: 一些小区别: 其他细节参考idea配置pdf 多线程: 基本概念: 多线程的优点: 何时需要多线程: 线程的创建和使用: 创建多线程的第一种方式: /** * ...