目录
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
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 客户端对象
- CSRedisClient _RedisMQ;
- // 连接 Redis
- public CSRedisClient ConnectCSRedis()
- {
- 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<IRedisMQ, RedisMQ>();
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
来源: http://www.bubuko.com/infodetail-3091040.html