问题:

  • 公司开了个新项目,算上我一共3个人。车间里机台通过流水线连通联动的玩意。一个管理控制系统连接各个机台和硬件。专机类型就有5种,个数差不多20个左右。

  • 软件规划的时候采用总分的结构,管理控制系统和专机子系统之间通过消息中间件通讯。本来也想TCP连接来着,但是开发时间不允许。而且每个系统都得写一遍这个玩意。

  • 消息中间件有很多个,比如 Kafka、RabbitMQ、RocketMQ等国内外的消息中间件。这些中间件无论宣称的多么轻量级都要啃一下,更要命的是就他娘三个人。而且后面还要这个鸡儿系统可复制。

  • 考虑到消息及时性、开发难易程度、维护简便性等因素后决定用Redis的pub/sub功能来实现.软件结构大概如类似结构。

可用性:

  • 作为消息通知属于安装了Redis就有的功能,因为Redis是用在系统中存储一些热数据,不用单独维护,在Windows中属于服务直接就开了。

  • 作为可以分布式集群使用的数据库,消息传递应该比较OK了。虽然使用的client-server,但是server-server已经很好了。料想client-server也不会差

  • 试验消息内容发送订阅的情况下,速度在30毫秒内,貌似可以。看其他博主说大于10K入队比较慢,但是可以不用入消息队列啊,用发布订阅。

  • .net 下一般使用ServiceStack.Redis,要命的是4.0以后收费,可以破解的但是不支持List<T>型的数据直接存取,想用只能变成JSON字符串存着。

  • 如果只是用订阅发布功能,不存储热数据或者不使用List<T>的数据可以使用4.0以上的版本。文末会贴上两个类型的下载包。想用其他的包也可以,我这里只说一种思路。

实现:

模块结构图展示如下

  1. public static class MSServer
  2. {
  3. // 定义一个object对象
  4. private static object objinstance = new object();
  5.  
  6. private static ServerState CurState = ServerState.Free;
  7.  
  8. static PooledRedisClientManager prcm;
  9.  
  10. private static string clientmake = string.Empty;
  11.  
  12. /// <summary>
  13. /// 连接的地址
  14. /// </summary>
  15. /// <param name="IP">地址127.0.0.1:6379</param>
  16. /// <param name="rechannels">接收通道 {"channel:1-13","channel:1-5"}</param>
  17. /// <returns></returns>
  18. public static int OpenServer(string IP ,string[] rechannels)
  19. {
  20. try
  21. {
  22. if (prcm == null)
  23. {
  24. lock (objinstance)
  25. {
  26. if (prcm == null)
  27. {
  28. prcm = CreateManager(IP, IP);
  29. CurState = ServerState.Init;
  30. return CreateLink(rechannels);
  31. }
  32. }
  33. }
  34. }
  35. catch
  36. {
  37. prcm = null;
  38. CurState = ServerState.Free;
  39. return -;
  40. }
  41. return ;
  42. }
  43.  
  44. private static int CreateLink(string[] SourceID)
  45. {
  46. if (CurState == ServerState.Init && SourceID.Length > )
  47. {
  48. try
  49. {
  50. using (IRedisClient Redis = prcm.GetReadOnlyClient())
  51. {
  52. clientmake = SourceID[];
  53. var info = Redis.GetClientsInfo().Where(i => i["name"] == clientmake).ToList();
  54. info.ForEach(i =>
  55. {
  56. Redis.KillClient(i["addr"]);
  57. });
  58. Redis.SetClient(clientmake);
  59. IRedisSubscription sc = Redis.CreateSubscription();
  60. Task.Run(() =>
  61. {
  62. try
  63. {
  64. sc.SubscribeToChannels(SourceID);
  65. }
  66. catch { }
  67. });
  68. sc.OnMessage += new Action<string, string>(showpub);
  69. }
  70. CurState = ServerState.Work;
  71. }
  72. catch
  73. {
  74. string message = string.Empty;
  75. prcm = null;
  76. CurState = ServerState.Free;
  77. return -;
  78. }
  79. return ;
  80. }
  81. else
  82. {
  83. return ;
  84. }
  85. }
  86.  
  87. public static Action<string, string> ReceiveMessage;
  88. static void showpub(string channel, string message)
  89. {
  90. if (ReceiveMessage != null)
  91. {
  92. ReceiveMessage(channel, message);
  93. }
  94. }
  95.  
  96. private static PooledRedisClientManager CreateManager(string writeHost, string readHost)
  97. {
  98. var redisClientConfig = new RedisClientManagerConfig
  99. {
  100. MaxWritePoolSize = ,//“写”链接池链接数
  101. MaxReadPoolSize = ,//“读”链接池链接数
  102. DefaultDb = ,
  103. AutoStart = true,
  104. };
  105. //读的客户端只能接受特定的命令,不能用于发送信息
  106. var RedisClientManager = new PooledRedisClientManager(
  107. new string[] { writeHost }//用于写
  108. , new string[] { readHost }//用于读
  109. , redisClientConfig);
  110. CurState = ServerState.Init;
  111.  
  112. return RedisClientManager;
  113. }
  114. /// <summary>
  115. /// 发送信息
  116. /// </summary>
  117. /// <param name="channel">通讯对象 "channel:1-13"</param>
  118. /// <param name="meesage">发送信息 "test send "</param>
  119. /// <returns>0 发送失败 1 发送成功 -1 连接损毁 检查网络后重建</returns>
  120. public static long PubMessage(string channel, string meesage)
  121. {
  122. if (CurState == ServerState.Work)
  123. {
  124. if (!string.IsNullOrEmpty(channel) && !string.IsNullOrEmpty(meesage))
  125. {
  126. try
  127. {
  128. using (IRedisClient Redis = prcm.GetClient())
  129. {
  130. Redis.SetClient(clientmake);
  131. return Redis.PublishMessage(channel, meesage);
  132. }
  133. }
  134. catch
  135. {
  136. prcm = null;
  137. CurState = ServerState.Free;
  138. return -;
  139. }
  140. }
  141. else
  142. {
  143. return ;
  144. }
  145. }
  146. else
  147. {
  148. return -;
  149. }
  150. }
  151. }
  152.  
  153. public enum ServerState
  154. {
  155. Free,
  156. Init,
  157. Work,
  158. Del
  159. }

有一个问题,就是连接远程的服务器时如果网络断开再重连,会残留没用的client ,这样如果网络断断续续的话,会留好多没有清除的客户端。

这个在3.0.504版本中Redis 中也有这个问题,不知道是基于什么考虑的。所以需要建立连接的时候,给个客户端名称,再初始化的时候删掉所有同类型的名称。

使用的时候大概类似操作 textbox2.text = "channel:1-5" .为了简便发布的和监听的都是本地的一个通道。

  1. private void button1_Click(object sender, EventArgs e)
  2. {
  3.  
  4. //11.1.7.152 192.168.12.173
  5. int result = ServerMS.MSServer.OpenServer("127.0.0.1:6379", new string[] { textBox2.Text });
  6. label1.Text = result.ToString();
  7. //1匿名事件
  8. ServerMS.MSServer.ReceiveMessage += new Action<string, string>(fuck);
  9.  
  10. if (result == )
  11. {
  12. //发送失败重新发送 检查 通道和字符串后重新发送
  13. }
  14. else if (result == )
  15. {
  16. //发送成功
  17. }
  18. else if (result == -)
  19. {
  20. //连接错误 需要 ServerMS.MSServer.OpenServer("192.168.12.173:6379", new string[] { textBox2.Text });
  21. }
  22.  
  23. }
  24.  
  25. void fuck(string channel, string message)
  26. {
  27. this.BeginInvoke(new Action(() =>
  28. {
  29. textBox4.Text = channel + message;
  30. }));
  31. }
  32. public bool sdfsd = true;
  33.  
  34. private void button3_Click(object sender, EventArgs e)
  35. {long result = ServerMS.MSServer.PubMessage(textBox2.Text, DateTime.Now.ToString("yyyyyMMddhhmmssfff"));
  36.  
  37. if (result == )
  38. {
  39. //发送失败重新发送
  40. }
  41. else if (result == )
  42. {
  43. //发送成功
  44. }
  45. else if (result == -)
  46. {
  47. //连接错误 需要 ServerMS.MSServer.OpenServer("192.168.12.173:6379", new string[] { textBox2.Text });
  48. }
  49. }

为了简便channel:是通道的固定命令 ,可以自定义channel:后面的内容,发送就有反馈。确保所有机台都接收到。

如果有断线的需要程序自己重连,接收通道的客户端不可以再给其他的使用,Redis上说Redis client 进入订阅模式时只能接受订阅发布等命令指令,不接受普通的存取和其他命令

所以如果需要在读取、写入、发布、执行其他的指令需要使用其他客户端,否则就出错了。跑了几天了上亿次的测试貌似没有出现什么问题。

发布订阅消息不会走AOF RDB只存在于内存中,即发即用,用完就没了。没在线就没了。需要考虑使用环境。

还用ping pong来确定连接状态,也可以自定义数据,使用场景要自己开发,要适合自己的才是好的。

下载:

4.0 dll

链接:https://pan.baidu.com/s/1966t0pduHxQXcxcxV3ZTeQ
提取码:js8p

5.8 dll不可以使用List<T>类型

链接:https://pan.baidu.com/s/1RFgY4V0ZO78Wvd7LOxr97g
提取码:bxh2

Redis 消息中间件 ServiceStack.Redis 轻量级的更多相关文章

  1. Redis客户端ServiceStack.Redis的简单使用

    在nuget中下载ServiceStack.Redis,但是运行之后会出现一个问题: Exception: "Com.JinYiWei.Cache.RedisHelper"的类型初 ...

  2. 关于 ServiceStack.Redis 4.0 License

    今天更新了框架中的Redis驱动ServiceStack.Redis,最新版本4.0.5.0. 在做简单压力测试时出现异常,提示每小时允许6000个请求. The free-quota limit o ...

  3. 『性能』ServiceStack.Redis 和 StackExchange.Redis 性能比较

    背景 近来,需要用到 Redis 这类缓存技术 —— MongoDB 和 Redis 没有进行过比较. 我也懒得在这些细节上 纠结那么多 —— 按照网友给出的文章,听从网友建议,选择 Redis. R ...

  4. servicestack.redis工具类

    using System;using System.Collections.Generic;using System.Linq;using ServiceStack.Redis;using Servi ...

  5. 使用ServiceStack.Redis实现Redis数据读写

    原文:使用ServiceStack.Redis实现Redis数据读写 User.cs实体类 public class User { public string Name { get; set; } p ...

  6. .Net使用Redis详解之ServiceStack.Redis(七)

    序言 本篇从.Net如何接入Reis开始,直至.Net对Redis的各种操作,为了方便学习与做为文档的查看,我做一遍注释展现,其中会对list的阻塞功能和事务的运用做二个案例,进行记录学习. Redi ...

  7. ServiceStack.Redis订阅发布服务的调用(Z)

      1.Redis订阅发布介绍Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式.发布者和订阅者之间使用频 ...

  8. serviceStack.Redis 在PooledRedisClientManager 中设置密码

    ServiceStack.Redis 是一个C#访问Redis的客户端,可以说可以通过它实现所有需要Redis-Cli的功能.但是今天我在主Redis 实例设置了访问密码,而在slave 上没有设置, ...

  9. ServiceStack.Redis订阅发布服务的调用

    1.Redis订阅发布介绍 Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式. 发布者和订阅者之间使用频 ...

随机推荐

  1. 素数&欧拉函数

    素数表 const int maxN找[1,maxN)内的素数 int prime[int I]第I个素数 const int maxN=1e5+5; int prime[maxN]; bool ma ...

  2. dispatch_async 的 block 中是否该使用_weak self

    问题分析 我看过很多文章关于在dispatch_async的block里面使用_weak self, 但是让我疑惑的是,以下代码是否需要必须使用_weak self, 因为我也看到了很多观点说,在有些 ...

  3. Golang项目部署

    文章来源:https://goframe.org/deploymen... 一.独立部署 使用GF开发的应用程序可以独立地部署到服务器上,设置为后台守护进程运行即可.这种模式常用在简单的API服务项目 ...

  4. mac OS nvm 常用命令

    nvm install stable ## 安装最新稳定版 node,当前是node v10.15.0 (npm v6.4.1) nvm install <version> ## 安装指定 ...

  5. OSChina 清明节乱弹 ——准备好纸巾了看乱弹

    2019独角兽企业重金招聘Python工程师标准>>> Osc乱弹歌单(2017)请戳(这里) [今日歌曲] @亚麻仔 :分享 范忆堂 的歌曲<酡颜 (夏热版)> 同一个 ...

  6. 使用 html5 FileReader 获取图片, 并异步上传到服务器 (不使用 iframe)

    为什么80%的码农都做不了架构师?>>>   原理: 1.使用FileReader 读取图片的base64编码 2.使用ajax,把图片的base64编码post到服务器. 3.根据 ...

  7. pod setup命令失败解决方法

    最近运行pod setup出现以下问题: remote: Compressing objects: 100% (34/34), done.error: RPC failed; curl 56 SSLR ...

  8. Netty(一):ByteBuf读写过程图解

    我们知道ByteBuf通过读写两个索引分离,避免了NIO中ByteBuffer中读写模式切换时,需要flip等繁琐的操作. 今天就通过一段测试代码以及图例来直观的了解下ByteBuf中的readInd ...

  9. OpenStack 删除instance 和其附加的volumes

    在openstack里面有时候删除instance时,volume无法跟着删除,可以自己编写脚本来实现, 脚本代码如下: #!/bin/bash for i in $(cat /root/host-d ...

  10. Vue.js 条件渲染 v-if、v-show、v-else

    v-if  v-if 完全根据表达式的值在DOM中生成或移除一个元素.如果v-if表达式赋值为false,那么对应的元素就会从DOM中移除:否则,对应元素的一个克隆将被重新插入DOM中. 1 2 3 ...