基于Redis消息的订阅发布应用场景

目录

  • 基于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
    {   //连接Redis
        CSRedisClient ConnectCSRedis();
        //订阅频道
        void SubscribeCSRedis(string ChannelName);
        //把message异步发布Redis的频道
        void PublishAsyncCSRedis(string channel, string message);
        //释放Redis
        void DisposeCSRedis();
        //订阅接受下来的msg的方法
        void Rcv(string Msg, string channel);
    }
}

4.3 接口实现如下

详细说明见注释

using System;
using CSRedis;
using IBMS.Infrastruct.Appsetting;

namespace IBMS.Infrastruct.Redis
{
    public class RedisMQ : IRedisMQ
    {
        //读取连接Redis字符串
        private readonly string connectRedis = Appsettings.app(new string[] { "AppSettings", "RedisCaching", "ConnectionString" });//按照层级的顺序,依次写出来     
        //定义一个Redis客户端对象
        static CSRedisClient _RedisMQ;
        //连接Redis
        public CSRedisClient ConnectCSRedis()
        {       
            //如果已经连接实例,直接返回
            if (_RedisMQ != null)
            {
                return _RedisMQ;
            }    
           return _RedisMQ = new CSRedisClient(connectRedis);
        }
        //释放Redis
        public void DisposeCSRedis()
        {
            _RedisMQ.Dispose();
        }
       //异步发布消息到Redis的某个频道
        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)));
        }

    }
}

4.4 ConfigureServices中依赖注入

在Startup.cs中的ConfigureServices方法进行依赖注入,如下。
services.AddScoped();

4.5 创建一个RedisMQ的消息对象

在Controller里定义创建一个消息对象,这一步的前提是需要依赖注入,依赖注入在某种意义上跟C语言的typedef有点像,将typedef会将控制权交给编译器,编译器定义新类型,然后程序运行之后就可以就可以随意通过新类型来定义对象。
IRedisMQ _RedisMQ =new RedisMQ();

4.6 实现层代码设计

// PUT: api/ControlConfig/5
[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 打开风扇按钮

5.2 RedisDesktopManager工具中观察

在RedisDesktopManager的命令行窗口中输入PSUBSCRIBE web,进行订阅web频道,如下

5.3 观察web频道输出信息

在前端控制了风扇打开操作之后如5.1,在RedisDesktopManager观察web频道输出信息

5.4 观察实际风扇效果

风扇实时打开。
备注:采集控制端跟设备端是基于TCP长连接组网方式,协议用的是基于MODBUS的变种,比如加入我们自己的包头包尾包类型等信息,这里不做展开


6 框架图

补上一张框架图,拖到浏览器新窗口,点击放大即可清晰浏览,采用亿图制作,以便更好理解。

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服务器可以订阅发布消息了,但是这样一来一些认证权限就需要做在前端了哦

你可能感兴趣的:(#,缓存,消息队列,搜索引擎,大学与Java那些年)