目录
- 基于Redis消息的订阅发布应用场景
- 1.应用背景
- 2.困境
- 2.1 锁表风险
- 2.2 实时性差
- 2.3 增加编程复杂性
- 2.4 实时效果
- 3.解决方案
- 3.1 前端传值给服务端
- 3.2 服务端通过消息传给采集控制端
- 4.详细代码设计
- 4.1 CSRedisCore
- 4.2 接口设计如下
- 4.3 接口实现如下
- 4.4 ConfigureServices中依赖注入
- 4.5 创建一个RedisMQ的消息对象
- 4.6 实现层代码设计
- 5.效果
- 5.1 打开风扇按钮
- 5.2 RedisDesktopManager工具中观察
- 5.3 观察web频道输出信息
- 5.4 观察实际风扇效果
- 6 框架图
- 7 GitHub
基于Redis消息的订阅发布应用场景
1.应用背景
在物联网采集管控系统中,前后端隔离的情况下,前端通过表单(比如按钮,开关,表格等)输入数据到数据库(比如MySql,通过WEBAPI服务端输入),然后采集控制端到数据库里去扫表取数据,将数据下发给物联网络中的终端设备(比如风扇控制板),从而来控制风扇的开跟关。
2.困境
采集控制端需要到数据库中去扫表。这个扫表操作会带来几个问题:
2.1 锁表风险
扫表会有锁表风险,当该DBContext被占用的时候,其他线程不能实时使用此DBContext。
2.2 实时性差
在物联网系统中,数据会非常多,比如有10000台设备,每台设备有100个采集控制点,则控制点最多可能会达到100W数据,这样去扫表,不仅占用DBContext上下文的时间会很长,而且实时性会很差。
2.3 增加编程复杂性
增加了采集服务端编程的复杂性。
2.4 实时效果
用户体验效果较差:客户点了开关控制风扇打开,然后底端设备需要很长时间才能真正打开。
3.解决方案
使用消息订阅发布方法。RabbitMQ比较重,故这里选用Redis的订阅发布功能,而且很多情况下Redis已经被作为缓存在引用,详见如下。
3.1 前端传值给服务端
前端将实时控制值以Restful API形式通过IP地址端口号+路由(比如:192.168.2.106:5000/ControlConfig)将此值传递给服务端。
3.2 服务端通过消息传给采集控制端
这里通过nuget获得CSRedisCore,来操作Redis的订阅发布功能。采集控制端订阅消息。服务端发布消息。这样操作达到了如下目的:2.1不用经过数据库消息的实时传递;2.2 实时性好;2.3 编程也简单;2.4 实时效果好。
4.详细代码设计
4.1 CSRedisCore
CSRedis 是 redis.io 官方推荐库,支持 redis-trib集群、哨兵、私有分区与连接池管理技术,简易 RedisHelper 静态类。
https://www.nuget.org/packages/CSRedisCore/
通过Nuget获得CSRedisCore库
4.2 接口设计如下
详细说明参考注释。
using CSRedis;
namespace IBMS.Infrastruct.Redis
{
public interface IRedisMQ
{
CSRedisClient ConnectCSRedis();
void SubscribeCSRedis(string ChannelName);
void PublishAsyncCSRedis(string channel, string message);
void DisposeCSRedis();
void Rcv(string Msg, string channel);
}
}
4.3 接口实现如下
详细说明见注释
using System;
using CSRedis;
using IBMS.Infrastruct.Appsetting;
namespace IBMS.Infrastruct.Redis
{
public class RedisMQ : IRedisMQ
{
private readonly string connectRedis = Appsettings.app(new string[] { "AppSettings", "RedisCaching", "ConnectionString" });
static CSRedisClient _RedisMQ;
public CSRedisClient ConnectCSRedis()
{
if (_RedisMQ != null)
{
return _RedisMQ;
}
return _RedisMQ = new CSRedisClient(connectRedis);
}
public void DisposeCSRedis()
{
_RedisMQ.Dispose();
}
public void PublishAsyncCSRedis(string channelName, string message)
{
_RedisMQ.PublishAsync(channelName, message);
}
public void Rcv(string channel, string Msg)
{
Console.WriteLine($"{DateTime.Now.ToLongDateString()}|Rcv:{channel},Msg:{Msg}");
}
public void SubscribeCSRedis(string ChannelName)
{
_RedisMQ.Subscribe((ChannelName, msg => Rcv(msg.Channel, msg.Body)));
}
}
}
在Startup.cs中的ConfigureServices方法进行依赖注入,如下。
services.AddScoped();
4.5 创建一个RedisMQ的消息对象
在Controller里定义创建一个消息对象,这一步的前提是需要依赖注入,依赖注入在某种意义上跟C语言的typedef有点像,将typedef会将控制权交给编译器,编译器定义新类型,然后程序运行之后就可以就可以随意通过新类型来定义对象。
IRedisMQ _RedisMQ =new RedisMQ();
4.6 实现层代码设计
[HttpPut]
public async Task Update([FromBody] ControlConfig ControlConfig)
{
_RedisMQ.ConnectCSRedis();
_RedisMQ.SubscribeCSRedis("web");
_RedisMQ.PublishAsyncCSRedis("web", $"add at{DateTime.Now}");
_RedisMQ.PublishAsyncCSRedis("web", $"{SerializeHelper.Serialize(ControlConfig)}");
Console.ReadKey();
_RedisMQ.DisposeCSRedis();
}
5.效果
5.1 打开风扇按钮
![](http://img.e-com-net.com/image/info8/441b394dd55648f9bf98622888f86f04.png)
5.2 RedisDesktopManager工具中观察
在RedisDesktopManager的命令行窗口中输入PSUBSCRIBE web,进行订阅web频道,如下
![](http://img.e-com-net.com/image/info8/47fa03df454944c08ed185a77010ca06.jpg)
5.3 观察web频道输出信息
在前端控制了风扇打开操作之后如5.1,在RedisDesktopManager观察web频道输出信息
![](http://img.e-com-net.com/image/info8/b6fa7a96283d42d581ae635050f6f4e8.png)
5.4 观察实际风扇效果
风扇实时打开。
备注:采集控制端跟设备端是基于TCP长连接组网方式,协议用的是基于MODBUS的变种,比如加入我们自己的包头包尾包类型等信息,这里不做展开
6 框架图
补上一张框架图,拖到浏览器新窗口,点击放大即可清晰浏览,采用亿图制作,以便更好理解。
![](http://img.e-com-net.com/image/info8/82d19594b4d24bc1b512aaae5160ede5.jpg)
7 GitHub
Demo地址:
https://github.com/JerryMouseLi/RedisMQDemo.git
原文:https://www.cnblogs.com/JerryMouseLi/p/11012839.html
评论列表
#1楼 2019-06-12 23:36 大石头
用Redis做mq的不多,赞一个
#2楼 2019-06-13 00:23 FreeSql
支持支持!
楼主记得不要重复创建CSRedisCore和销毁,尽可能创建一次重复使用。
#3楼 [楼主] 2019-06-13 09:45 JerryMouseLi
@ 大石头
石头哥,感谢点赞,我有你微信,微信名与风,之前咱们聊过。
#4楼 2019-06-13 09:46 黑猫警长4399
mark
#5楼 2019-06-13 09:49 水木桶
redis做mq,请问数据丢失问题如何解决的?
#6楼 [楼主] 2019-06-13 09:51 JerryMouseLi
@ 水木桶
数据丢失?我这里实时控制了之后,服务端在发布之前或者之后可以记录,甚至可以做事件溯源的。
#7楼 [楼主] 2019-06-13 09:54 JerryMouseLi
@ FreeSql
感谢您提出宝贵意见,有什么好的方法可以操作创建一次重复使用呢?恳请指教哦。
#8楼 [楼主] 2019-06-13 09:55 JerryMouseLi
@ 黑猫警长4399
感谢,多提宝贵意见哦。
#9楼 2019-06-13 10:34 烟灰灰
用pub/sub有问题。
比如这个流程:sub挂了 -> pub发消息 -> sub启动,这样就丢消息了。
redis后面出了stream,带持久化的,会好一点
#10楼 [楼主] 2019-06-13 10:47 JerryMouseLi
@ 烟灰灰
您好,感谢您的评论,确实会存在您说的问题,但是windows版本好像还没有stream功能,linux上会有。然后我们这样简单数字量的控制输出刚好够用。
#11楼 2019-06-13 10:53 高海东
有运行的demo例子开源吗
#12楼 2019-06-13 11:03 wdwwtzy
https://www.nuget.org/packages/csredis/
这个才是官方推荐的哈。
你那个国内作者做的,虽然也不错。
#13楼 [楼主] 2019-06-13 11:16 JerryMouseLi
@ 高海东
您好,刚把Demo上传到git如下:https://github.com/JerryMouseLi/RedisMQDemo.git
#14楼 [楼主] 2019-06-13 11:18 JerryMouseLi
@ wdwwtzy
您好,您发的是基于.Net。我现在的应用是基于.Net Core的
#15楼 2019-06-13 11:21 cgyqu
signalr多服务器直接通讯也是这种模式,哈哈,是不是可以直接拿来用
#16楼 [楼主] 2019-06-13 11:32 JerryMouseLi
@ cgyqu
您好,刚好接下来要调SignalR,SignalR我主要想用把服务端的数据传到前端显示,比如地图页要显示所有设备的状态告警,比如会有5K台设备,我就想用SignalR 每秒传500的数据量给地图页分批次更新,这样前端web不会等待卡死。您说的signalr多服务器是指?还请作详细说明以作交流。直接拿来用是指用什么呢?
#17楼 2019-06-13 12:55 wdwwtzy
@ JerryMouseLi
你也不是官方的哈,容易误导
#18楼 [楼主] 2019-06-13 13:06 JerryMouseLi
@ wdwwtzy
您好,https://www.nuget.org/packages/CSRedisCore/ https://www.nuget.org/packages/csredis/这两个链接的路由看上去都是官方的呀?
#19楼 2019-06-13 13:56 Sam Xiao
@ wdwwtzy
到底那个才是官方的啊?你也不贴个地址出来。
#20楼 [楼主] 2019-06-13 14:16 JerryMouseLi
@ Sam Xiao
您好,https://github.com/ctstone/csredis这个是csredis 支持.NET 国外的人开发的;
https://github.com/2881099/csredis 这个是csrediscore,本项目采用的,支持.NETCore or .NetFramework 4.5+,本项目在,NetCore环境下使用,国人开发。
#21楼 2019-06-13 14:39 奋斗中
也可以使用mqtt,直接web前段 发布,设备控制端 订阅
#22楼 [楼主] 2019-06-13 14:54 JerryMouseLi
@ 奋斗中
如果是这种操作的话前端应该还需要启动node.js 调用类似https://github.com/0123cf/MQTT.js 然后设备端添加mqtt连接协议栈就可以了
#23楼 2019-06-13 19:51 vbfool
类似的场景我们也是用mqtt的server去转发的,有时候感觉为这个操作专门定制一个发布订阅系统也不为过。
#24楼 2019-06-13 19:53 老张的哲学
可以可以,你也准备写系列文章了,帅气,你可以总结个系列文章总目录,我放到我博客右侧公告里。
另外,我在考虑,你这个业务需要我怎么融到我的core项目里,你感觉有哪个场景可以使用?
#25楼 [楼主] 2019-06-13 20:34 JerryMouseLi
@ vbfool
是的,实时性很重要。实时性好了,用户体验就好。
#26楼 [楼主] 2019-06-13 20:38 JerryMouseLi
@ 老张的哲学
我的系列可以命名物联网服务端?场景的话就是数据量会比较大,扫表不合适,然后对实时性要求比较高。
#27楼 2019-06-13 21:04 C#小学徒
@ JerryMouseLi
博主,有关SignalR的总结,能总结一下呀,我在使用中遇到好多坑
#28楼 [楼主] 2019-06-14 07:47 JerryMouseLi
@ C#小学徒
我大概下周开始调试SignalR相关的,3天调后端,2天调前端,1天级联调。还剩一天如果你需要可以写篇博客。你也可以参照下文https://www.cnblogs.com/laozhang-is-phi/p/netcore-vue-signalr.html 这篇文章也详细介绍了SignalR的使用,如果还有需要你再留言,我视情况写篇博客哈。
#29楼 [楼主] 2019-06-15 15:29 JerryMouseLi
@ FreeSql
您好,您是CSRedis的作者么,我已经将CSRedisCore创建一次重复使用了。用的静态单例模式。
#30楼 2019-06-16 22:17 奋斗中
@ JerryMouseLi
mqtt.js是可以直接使用的,可以不用启动node.js,我在项目中就是这么使用的
The MQTT client for Node.js and the browser
并且.net core 下有MQTT broker的库,https://github.com/chkr1011/MQTTnet
#31楼 [楼主] 2019-06-17 08:15 JerryMouseLi
@ 奋斗中
那也就是直接mqtt.js向MQTT broker服务器可以订阅发布消息了,但是这样一来一些认证权限就需要做在前端了哦