哼哧哼哧半年,优化改进了一个运维开发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. adb shell 查看当前与用户交互的 activity

    adb shell dumpsys activity activities | grep mActivityComponent

  2. introduction-to-64-bit-assembly

    introduction-to-64-bit-assembly NASM - The Netwide Assembler x86-64 下函数调用及栈帧原理 汇编语言基本概念简介 mycode

  3. struts框架返回json数据

    设置返回result类型为json格式 <package name="cn.konngo.action" namespace="/" extends=&q ...

  4. Shiro03

    1.shiro授权角色.权限 2.Shiro的注解式开发 shiro权限思路 授权 ShiroUserMapper中定义两个方法 // 通过用户ID查询角色 Set<String> get ...

  5. JDK方法区、元空间区别 & String.intern相关面试题

    一.方法区.永久代.元空间 1.方法区.永久代 方法区也是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类信息.常量.静态变量.即时编译器编译后的代码等数据.方法区域又被称为"永久代& ...

  6. 一个简单的URL访问权限校验

    前言 目前最流行的两大安全框架:SpringSecruity.Shiro 权限控制,无非就是:前端控件是否可见.是否允许请求/访问URL 本文分享一个简单的URL访问权限校验,支持/./*./**等情 ...

  7. ubantu硬盘不足,无法启动

    我的ubantu虚拟机经过我一顿操作后,就起不来了.然后经过多方询问,广集天下良方,最终发现是由于分配的硬件空间不足导致的.现象如下: 通过查看 root@ubantu:/snap# df -h Fi ...

  8. Tensorflow保存神经网络参数有妙招:Saver和Restore

    摘要:这篇文章将讲解TensorFlow如何保存变量和神经网络参数,通过Saver保存神经网络,再通过Restore调用训练好的神经网络. 本文分享自华为云社区<[Python人工智能] 十一. ...

  9. Java中HashCode()和equals()的作用

    引言 我们知道Java中的集合(Collection)大致可以分为两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重复:后者元素无序,但元素不可重复. 这里就引出一个问题: ...

  10. 机器学习——EM算法

    1 数学基础 在实际中,最小化的函数有几个极值,所以最优化算法得出的极值不确实是否为全局的极值,对于一些特殊的函数,凸函数与凹函数,任何局部极值也是全局极致,因此如果目标函数是凸的或凹的,那么优化算法 ...