哼哧哼哧半年,优化改进了一个运维开发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。

Github Demo

SignalR 在React/GO技术栈的生产应用的更多相关文章

  1. webpack+babel+react+antd技术栈的基础配置

    webpack+babel+react+antd技术栈的基础配置 前段时间使用webpack+babel+react+antd做了一套后台管理系统,刚开始被一大堆的新知识压的喘不过气来,压力挺大的.还 ...

  2. 用react系列技术栈实现的demo整合系统

    引子 学生时代为了掌握某个知识点会不断地做习题,做总结,步入岗位之后何尝不是一样呢?做业务就如同做习题,如果‘课后’适当地进行总结,必然更快地提升自己的水平. 由于公司采用的react+node的技术 ...

  3. 一个基于React整套技术栈+Node.js的前端页面制作工具

    pagemaker是一个前端页面制作工具,方便产品,运营和视觉的同学迅速开发简单的前端页面,从而可以解放前端同学的工作量.此项目创意来自网易乐得内部项目nfop中的pagemaker项目.原来项目的前 ...

  4. 重谈react优势——react技术栈回顾

    react刚刚推出的时候,讲react优势搜索结果是几十页. 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文. 今天,再谈一遍react优 ...

  5. react技术栈实践(2)

    本文来自网易云社区 作者:汪洋 这时候还没完,又有两个问题引出来了. 按照上面的配置,第三方库 antd 竟然也被编译了,导致样式失败. react中,一旦包裹了子组件,子组件没办法直接使用 styl ...

  6. 从开发一款基于Vue技术栈的全栈热重载生产环境脚手架,我学到了什么

    浏览文章前 这一期,我分享给大家三点看源码的小技巧,这也是从别的大佬那总结的. 被反复使用的代码 这样的代码是一个软件的重点函数,一个大神的写法有很多精华值得学习. 穿越时间的代码 如果一段代码10年 ...

  7. 通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? .Net Web开发技术栈

    通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core?   什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念 ...

  8. 一个人的 ClojureScript 技术栈

    作者:题叶链接:https://zhuanlan.zhihu.com/p/24425284来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.今天(昨天)分享完关于 Cloj ...

  9. .Net Web开发技术栈

    有很多朋友有的因为兴趣,有的因为生计而走向了.Net中,有很多朋友想学,但是又不知道怎么学,学什么,怎么系统的学,为此我以我微薄之力总结归纳写了一篇.Net web开发技术栈,以此帮助那些想学,却不知 ...

随机推荐

  1. 百闻不如一试——公式图片转Latex代码

    写博客时,数学公式的编辑比较占用时间,在上一篇中详细介绍了如何在Markdown中编辑数学符号与公式. https://www.cnblogs.com/bytesfly/p/markdown-form ...

  2. 微信小程序--聊天室小程序(云开发)

    微信小程序 -- 聊天室小程序(云开发) 从微信小程序开发社区更新watch接口之后,一直在构思这个项目.项目已经完成很久,但是一直都没有空写一篇博客记录展示一下. 开源地址 wx-cloud-im: ...

  3. TCP连接中的状态

    1. 正常状态转换 我们用图 3-13 来显示在正常的 TCP 连接的建立与终止过程中,客户与服务器所经历的不同状态.读者可以对照图 3-12 来阅读,使用图 3-12 的状态图来跟踪图 3-13 的 ...

  4. .ssh/config 常用配置

    不用每次都 -i 指定密钥,且避免连接自动断开 ControlMaster auto ControlPath ~/.ssh/connection-%r@%h:%p ControlPersist 4h ...

  5. Python - break、continue 的使用

    前置知识 break.continue 会结合循环使用的,所以要先学会循环哦 python 提供了两种循环语句 for 循环:https://www.cnblogs.com/poloyy/p/1508 ...

  6. Mybatis-Plus常用的查询方法--看这一篇就够了!!!

    前言: Mybatis-Plus作为Mybatis的增强,自己封装了很多简单还用的方法,来解脱自己写sql! 对于项目的搭建小编就不在说了,可以参考: SpringBoot+Mybatis-Plus的 ...

  7. 将给定数据源生成静态HTML页面持久化到项目之外的硬盘

    一.java代码 设置好数据源map Map<String,String> map=new HashMap<>(); map.put("knowledgeName&q ...

  8. 数据治理中Oracle SQL和存储过程的数据血缘分析

    数据治理中Oracle SQL和存储过程的数据血缘分析   数据治理中的一个重要基础工作是分析组织中数据的血缘关系.有了完整的数据血缘关系,我们可以用它进行数据溯源.表和字段变更的影响分析.数据合规性 ...

  9. Nginx-初见

    目录 产品出现瓶颈? 什么是Nginx? Nginx作用 正向代理 反向代理 负载均衡策略(Nignx) 轮询 加权轮询 IP hash 动静分离 参考链接 产品出现瓶颈? 项目刚刚上线的时候,并发量 ...

  10. vue 动态ip配置,避免重复打包

    目前比较流行的打包大都是在vue.config.js配置代理,然后在根目录新建.env.xxx文件配置正式环境,测试环境,开发环境等用于打包时配置不同的访问地址,作为一名随波逐流的前端开发,我也是这么 ...