哼哧哼哧半年,优化改进了一个运维开发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. Learning ROS: Managing System dependencies

    Download and install the system dependencies for turtlesim: roscd turtlesim cat package.xml rosdep i ...

  2. 如何实现LRU缓存

    大家好,我是程序员学长,今天我们来聊一聊LRU缓存问题. Tips: LRU在计算机软件中无处不在,希望大家一定要了解透彻. 问题描述 设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设 ...

  3. vue + WangEnduit

    components 注册组件 <template lang="html"> <div class="editor"> <div ...

  4. java 线程基础篇,看这一篇就够了。

    前言: Java三大基础框架:集合,线程,io基本是开发必用,面试必问的核心内容,今天我们讲讲线程. 想要把线程理解透彻,这需要具备很多方面的知识和经验,本篇主要是关于线程基础包括线程状态和常用方法. ...

  5. 算法:实现strStr(),字符串indexOf方法

    描述 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返回  -1. 个人思路: ...

  6. CentOS 安装制定版本docker

    # 1) 配置镜像仓库 curl -o /etc/yum.repos.d/Docker-ce-Ali.repo https://mirrors.aliyun.com/docker-ce/linux/c ...

  7. Sentry Web 性能监控 - Trends

    系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...

  8. Android使用百度语音识别api代码实现。

    第一步 ① 创建平台应用 点击百度智能云进入,没有账号的可以先注册账号,这里默认都有账号了,然后登录. 然后左侧导航栏点击找到语音技术 然后会进入一个应用总览页面, 然后点击创建应用 立即创建 点击查 ...

  9. tcpdump使用手册

    tcp使用手册 格式: tcpdump [选项] [过滤条件] 选项: -i eth0 #网卡接口 -A #以ASCII码格式阅读 -w file #下载抓取的数据包 -r file #上传数据包 - ...

  10. SpringMVC执行流程总结

    SpringMVC 执行流程: 用户发送请求至前端控制器 DispatcherServlet DispatcherServlet 收到请求调用处理映射器 HandlerMapping 处理映射器根据请 ...