分布式消息总线,基于.NET Socket Tcp的发布-订阅框架之离线支持,附代码下载
一、分布式消息总线以及基于Socket的实现
在前面的分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载一文之中给大家分享和介绍了一个极其简单也非常容易上的基于.NET Socket Tcp 技术实现的分布消息总线,也是一个简单的发布订阅框架:
并且以案例的形式为大家演示了如何使用这个分布式消息总线架构发布订阅架构模式的应用程序,在得到各位同仁的反馈的同时,大家也非常想了解订阅者离线的情况,即支持离线构发布订阅框架。
二、离线架构
不同于订阅者、发布者都同时在线的情况,支持订阅者离线,架构将有所变化,如下图所示:
也会比原先的结构将更加复杂,其中需要处理以下两个关键点:
1)订阅者的持久化存储。
2)订阅者离线之后其所订阅消息的持久存储。
三、解决方案
为解决消息总线的离线支持机制,我们在Socket 框架之中增加了一个接口ISubscribeStorager:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace EAS.Messages
7: {
8: /// <summary>
9: /// 消息订阅存储接口。
10: /// </summary>
11: public interface ISubscribeStorager
12: {
13: /// <summary>
14: /// 持久化订阅。
15: /// </summary>
16: /// <param name="subscriber">订阅者。</param>
17: /// <param name="topic">消息主题。</param>
18: void Subscribe(string subscriber, string topic);
19:
20: /// <summary>
21: /// 持久化退订。
22: /// </summary>
23: /// <param name="subscriber">订阅者。</param>
24: /// <param name="topic">消息主题。</param>
25: void Unsubscribe(string subscriber, string topic);
26:
27: /// <summary>
28: /// 装载订阅信息。
29: /// </summary>
30: /// <returns>系统之中的订阅清单。</returns>
31: List<SubscribeItem> LoadSubscribes();
32:
33: /// <summary>
34: /// 写入消息。
35: /// </summary>
36: /// <param name="subscriber">订阅者。</param>
37: /// <param name="message">消息对象。</param>
38: void Write(string subscriber, QueueMessage message);
39:
40: /// <summary>
41: /// 读消息。
42: /// </summary>
43: /// <param name="subscriber">订阅者。</param>
44: /// <param name="message">消息对象。</param>
45: /// <returns>成功读取返回true,否则返回false。</returns>
46: bool Read(string subscriber, out QueueMessage message);
47: }
48: }
ISubscribeStorager共提供持久化订阅持久化消息存储共五个函数,其中:
LoadSubscribes:服务端初始化时读取所有的离线订阅关系,即那个订阅都订阅那那个主题。
Subscribe:持久化订阅者,当订阅才上线订阅消息时,持久化订阅关系,供离线检测之用。
Unsubscribe:持久化取消订阅,当订阅者退订消息时,从持久化订阅关系之中删除。
Write:当订阅者离线时,把订阅消息写入持久化存储。
Read:当离线订阅者上线时,从持久存储之中读取一条消息向其发送。
ISubscribeStorager:可以选择自己实现这个接口,以建立满足自己规则的离线存储机制,当然在AgileEAS.NET SOA 中间件之中提供了两种离线存储机制,存储于数据库和存储于MSMQ,下面向大家介绍一下这两种内置实现。
四、两种内置离线存储机制
在AgileEAS.NET SOA 中间件平台之中提供了两个ISubscribeStorager的实现,基于数据库的离线订阅存储实现EAS.Messages.DbSubscribeStorager和基于MSMQ的离线订阅存储实现EAS.Messages.MsmqSubscribeStorager。
EAS.Messages.DbSubscribeStorager:存储订阅关系在messageSubscribe.Config文件之中,消息存储在关系数据库SOA_SUBSCRIBEEVENTS表之中,使用前必须要建立相应的表结构,以下是SQL Server的DDL脚本:
1: CREATE TABLE [SOA_SUBSCRIBEEVENT](
2: [GUID] [varchar](36) NOT NULL,
3: [SUBSCRIBER] [nvarchar](128) NOT NULL,
4: [TOPIC] [nvarchar](128) NOT NULL,
5: [BODY] [image] NULL,
6: [FCTIME] [datetime] NOT NULL,
7: CONSTRAINT [PK_SOA_SUBSCRIBEEVENT] PRIMARY KEY CLUSTERED
8: (
9: [GUID] ASC
10: )
11: )
目前理论上支持SQLServer 、Mysql、ORACLE、Sqlite四种数据库结构,具体建表脚本请自行参考相应资料书写,也可以使用AgileEAS.NET SOA中间件所提供的数据库初始化工具创建。
EAS.Messages.MsmqSubscribeStorager:存储订阅关系在messageSubscribe.Config文件之中,消息存储Msmq消息队列之中,使用之前请确保机器上安装了MSMQ消息对列。
五、关于自定义实现ISubscribeStorager
有兴趣的朋友可以自定义实现接口ISubscribeStorager,这样就可以按自己的规则进行存储,比如把离线消息存储到mongodb、Redis、或者直接存储在文件之中,或者其他更多的实现规则,在此就不一一介绍,如有相关兴趣,请联系作者,如确有必要需要给在家介绍一下如何实现,将会另开一文本介绍如何自定义实现ISubscribeStorager接口。
六、改进在线例子支持离线
还是跟上次一样,以案例为在家展示一下怎么进行离线消息,就不重新开始例子,对原有例子做一些改进,改进后例子如下:
其中在原有项目的基础上增加了:Demo.Subscriber1和Demo.Subscriber2项目,其项目配置代码、配置文件基本上同Demo.Subscriber一样,其中唯一的差别在于,Demo.Subscriber1和Demo.Subscriber2向服务器提交订阅的时候都增加一个另friendName参数,其使用IMessageBus接口的以下订阅函数:
1: /// <summary>
2: /// 订阅消息。
3: /// </summary>
4: /// <param name="subscriber">订阅者。</param>
5: /// <param name="friendName">订阅者名称,用于处理离线订阅。</param>
6: /// <param name="topic">主题。</param>
7: /// <param name="notifyHandler">订阅通知。</param>
8: void Subscribe(object subscriber,string friendName ,string topic, MessageNotifyHandler notifyHandler);
Demo.Publisher项目为发布者代码。
Demo.Subscriber项目为订阅者代码。
Demo.Server项目为服务端代码。
Demo.Subscriber1项目之中,其Program.cs代码如下:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Windows.Forms;
5: using EAS.Messages;
6:
7: namespace Demo.Subscriber1
8: {
9: class Program
10: {
11: static void Main(string[] args)
12: {
13: var container = EAS.Objects.ContainerBuilder.BuilderDefault();
14: var bus = container.GetComponentInstance("MessageBus") as IMessageBus;
15: System.Console.WriteLine("Subscriber1");
16:
17: bus.Subscribe(new Program(), "Subscriber1", Demo.Messages.Topics.DEMO_TOPIC, MessageNotify);
18: System.Console.ReadLine();
19: }
20:
21: static void MessageNotify(object m)
22: {
23: Demo.Messages.Message message = m as Demo.Messages.Message;
24: System.Console.WriteLine(string.Format("Subscribe:{0}", message.ID));
25: }
26: }
27: }
其中bus.Subscribe(new Program(), "Subscriber1", Demo.Messages.Topics.DEMO_TOPIC, MessageNotify);在订阅消息的时候给了一个friendName为Subscriber1,Demo.Subscriber2与Demo.Subscriber1项目的唯一的差别就是此处为Subscriber2.
我们使用内置的EAS.Messages.DbSubscribeStorager,则不需要修改服务端的代码,只需要修改服务端的配置文件如下:
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: <configSections>
4: <section name="eas" type="EAS.ConfigHandler,EAS.MicroKernel" />
5: </configSections>
6: <startup useLegacyV2RuntimeActivationPolicy="true">
7: <supportedRuntime version="v4.0"/>
8: </startup>
9: <eas>
10: <objects>
11: <!--数据库连接-->
12: <object name="DbProvider" assembly="EAS.Data" type="EAS.Data.Access.SqlClientDbProvider" LifestyleType="Thread">
13: <property name="ConnectionString" type="string" value="Data Source=.;Initial Catalog=eas_db;Integrated Security=SSPI;Connect Timeout=30" />
14: </object>
15: <!--数据访问器-->
16: <object name="DataAccessor" assembly="EAS.Data" type="EAS.Data.Access.DataAccessor" LifestyleType="Thread">
17: <property name="DbProvider" type="object" value="DbProvider"/>
18: <property name="Language" type="object" value="TSqlLanguage"/>
19: </object>
20: <!--ORM访问器-->
21: <object name="OrmAccessor" assembly="EAS.Data" type="EAS.Data.ORM.OrmAccessor" LifestyleType="Thread">
22: <property name="DataAccessor" type="object" value="DataAccessor"/>
23: </object>
24: <!--查询语言-->
25: <object name="TSqlLanguage" assembly="EAS.Data" type="EAS.Data.Linq.TSqlLanguage" LifestyleType="Thread"/>
26: <!--消息持久化-->
27: <object name="SubscribeStorager" assembly="EAS.SOA.BootStrap" type="EAS.Messages.DbSubscribeStorager" LifestyleType="Singleton"/>
28: <!--日志管理-->
29: <object name="Logger" assembly="EAS.MicroKernel" type="EAS.Loggers.TextLogger" LifestyleType="Singleton">
30: <property name="RootPath" type="string" value="G:\App.Work\Pub_Sub\Offline\Publish\logs" />
31: </object>
32: </objects>
33: </eas>
34: </configuration>
在配置文件的IOC配置之中我们配置了消息存储对象以及其所依赖的数据库访问对象、Linq查询语言表达式,另外需要说明的是,我们需要把配置文件之中所涉及的EAS.Data.dll、EAS.SOA.BootStrap.dll复制到编译输出Publish,这两个文件可以从AgileEAS.NET SOA 中间件平台发布包之中寻找,本案例的下载压碎包之中会包括这两个文件。
有关于基于Msmq的配置,只需要修改配置文件如下:
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: <configSections>
4: <section name="eas" type="EAS.ConfigHandler,EAS.MicroKernel" />
5: </configSections>
6: <startup useLegacyV2RuntimeActivationPolicy="true">
7: <supportedRuntime version="v4.0"/>
8: </startup>
9: <eas>
10: <objects>
11: <!--消息持久化-->
12: <object name="SubscribeStorager" assembly="EAS.SOA.BootStrap" type="EAS.Messages.MsmqSubscribeStorager" LifestyleType="Singleton"/>
13: <!--日志管理-->
14: <object name="Logger" assembly="EAS.MicroKernel" type="EAS.Loggers.TextLogger" LifestyleType="Singleton">
15: <property name="RootPath" type="string" value="G:\App.Work\Pub_Sub\Offline\Publish\logs" />
16: </object>
17: </objects>
18: </eas>
19: </configuration>
到此为止,所有代码均已完成,是不是很简单,接下来,我们跑起来验证一下效果。
七、验证效果
我们在编译输入目录Publish下先启动Demo.Server.exe,再各启动Demo.Subscriber.exe、Demo.Subscriber1.exe、Demo.Subscriber2.exe,再启动一个Demo.Publisher.exe,在Demo.Publisher.exe控制台按回车键:
目前程序三个订阅者都是在线的,Demo.Publisher发布了三条消息,三个订阅者都收到了三条消息,那么我们关闭Demo.Subscriber2之后再由Demo.Publisher发布两条消息:
然后我们再启动Demo.Subscriber2,看是否还能收到其离线之后由Demo.Publisher发布的两条消息:
OK,到此为止。
八、源代码下载
本程序的源代码已上传到服务器,请通过http://112.74.65.50/downloads/eas/Demo.Pub_Sub_Offline.rar进行下载,如果在开发过程之中想要了解更多有关Socket通信框架以及更多AgileEAS.NET SOA中间件平台的技术资源,请通过AgileEAS.NET SOA 网站:http://www.smarteas.net的最新下载栏目进行下载。
九、问题反馈
麻烦大家在通过视频进行学习的时候能及时把问题反馈给楼主,或者有什么需要改进的一些建议都请向楼主直接反馈,以下是联系方式:
AgileEAS.NET网站:http://www.agileeas.net
官方博客:http://eastjade.cnblogs.com
github:https://github.com/agilelab/eas
楼主QQ:47920381
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
120661978(AgileEAS.NET 平台交流)/上限1000人
邮件:james@agilelab.cn,mail.james@qq.com,
电话:18629261335。
分布式消息总线,基于.NET Socket Tcp的发布-订阅框架之离线支持,附代码下载的更多相关文章
- 基于.NET Socket Tcp的发布-订阅框架
基于.NET Socket Tcp的发布-订阅框架 一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已 ...
- 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载
一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经完成,在通常的情况下,开发人中都是在使用者B所使用 ...
- Redisson 分布式锁实现之前置篇 → Redis 的发布/订阅 与 Lua
开心一刻 我找了个女朋友,挺丑的那一种,她也知道自己丑,平常都不好意思和我一块出门 昨晚,我带她逛超市,听到有两个人在我们背后小声嘀咕:"看咱前面,想不到这么丑都有人要." 女朋友 ...
- 基于 Golang 构建高可扩展的云原生 PaaS(附 PPT 下载)
作者|刘浩杨 来源|尔达 Erda 公众号 本文整理自刘浩杨在 GopherChina 2021 北京站主会场的演讲,微信添加:Erda202106,联系小助手即可获取讲师 PPT. 前言 当今时 ...
- eShopOnContainers学习系列(三):RabbitMQ消息总线实践
今天研究了下eShopOnContainers里的RabbitMQ的使用,在项目里是以封装成消息总线的方式使用的,但是仍然是以其发布.订阅两个方法作为基础封装的,我们今天就来实际使用一下. 为了简单起 ...
- 物联网网关开发:基于MQTT消息总线的设计过程(上)
道哥的第 021 篇原创 目录 一.前言 二.网关的作用 2.1 指令转发 2.2 外网通信 2.3 协议转换 2.4 设备管理 2.5 边沿计算(自动化控制) 三.网关内部进程之间的通信 3.1 网 ...
- 分布式消息通信(ActiveMQ)
分布式消息通信(ActiveMQ) 应用场景 异步通信 应用解耦 流量削峰 # ActiveMQ安装 下载 http://activemq.apache.org/ 压缩包上传到Linux系统 apac ...
- [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)
一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...
- Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇
目前业界流行的分布式消息队列系统(或者可以叫做消息中间件)种类繁多,比如,基于Erlang的RabbitMQ.基于Java的ActiveMQ/Apache Kafka.基于C/C++的ZeroMQ等等 ...
随机推荐
- mysql代码执行漏洞
mysql (5.7, 5.6, 和 5.5版本)的所有默认安装配置,包括最新的版本,攻击者可以远程和本地利用该漏洞.该漏洞需要认证访问MYSQL数据库(通过网络连接或者像phpMyAdmin的we ...
- 使用Cocos2d-x实现微信“天天爱消除”炫耀button特效
引言Cocos2d-x引擎中有很多Action,这样可以方便的让开发者调用相应的Action去完成一些动作,例如:移动,弹跳,淡入淡出等.可在实际的开发过程中,由于游戏的需要,显然地,引擎自带的Act ...
- mysql存储过程和存储函数
mysql存储过程和存储函数 存数函数代码示例: DROP PROCEDURE IF EXISTS calc_ci_day_suc_rate; delimiter // CREATE FUNCTION ...
- Oracle 如何扩展表空间
第一步:查看ORACLE表空间的使用情况:SELECT DBF.TABLESPACE_NAME, DBF.TOTALSPACE "总量(M)", DBF.T ...
- VSTO PowerPoint 代码删除Shape后再恢复出现无法再次获取的问题
做PowerPoint的VSTO插件项目,遇到个很奇怪的问题,当代码执行删除某些Shape时,没问题,但是操作Undo也就是恢复后,无法再次获取到之前删除的对象,这种情况只在Office2007中出现 ...
- php编译 :virtual memory exhausted: Cannot allocate memory
有时候用vps建站时需要通过编译的方式来安装主机控制面板.对于大内存的VPS来说一般问题不大,但是对于小内存,比如512MB内存的VPS来说,很有可能会出现问题,因为编译过程是一个内存消耗较大的动作. ...
- [BZOJ3729]Gty的游戏
[BZOJ3729]Gty的游戏 试题描述 某一天gty在与他的妹子玩游戏.妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问将某个节点的子树中的石子移动 ...
- ArrayList和HashSet的Contains()方法(转)
来源: ArrayList和HashSet的Contains()方法 笔试题: package com.champion.test.exam; import java.util.ArrayList; ...
- Android下LayoutInflater的使用
在我们想XML布局文件转换为View对象的时候.我们都会使用LayoutInflate对象.顾名思义咋一眼就能看出来他是布局填充器.那么接下来看看LayoutInfalte的使用 总体分为 Layou ...
- Spring框架学习(二)
一.Spring IOC参数值注入 1.注入基本值 1.1)<value></value>元素可以通过字符串指定属性或构造参数的值.容器将字符串从java.lang.Strin ...