我们接上篇文章,继续来介绍Shuttle ESB的Pub/Sub模式。
上一篇文章中,我们已经用语言描述了一个用ESB实现的场景,下面给我给出具体的代码实现。
首先,我们需要了解一下Shuttle ESB各个dll的功能:
Shuttle.Core.Data:轻量级框架,使用ADO.NET的工厂和接口
Shuttle.Core.Domain:提供事件调度支持
Shuttle.Core.Host:通用主机,它能在控制台应用或者windows服务中运行
Shuttle.Core.Infrastructure:Shuttle ESB的基础服务
Shuttle.ESB.Core:Shuttle的核心实现
Shuttle.ESB.Modules:Shuttle的扩展服务
Shuttle.ESB.MSMQ:Shuttle基于微软消息队列的实现
Shuttle.ESB.SqlServer:基于SqlServer数据库的表队列
1、消息实体
创建项目PublishSubscribe.Message,并添加OrderCompletedEvent和WorkDoneEvent两个消息事件实体(至于为什么是两个消息实体,我也已经介绍过了),使用两个消息事件实体,是为了防止死循环的出现。
a、OrderCompletedEvent事件消息实体
using System;
namespace PublishSubscribe.Messages
{
public class OrderCompletedEvent
{
public Guid OrderId { get; set; }
public String coment { get; set; }
}
}
b、WorkDoneEvent事件消息实体
namespace PublishSubscribe.Messages
{
public class WorkDoneEvent
{
public string Comment { get; set; }
}
}
2、消息发布端
消息发布端,主要功能是:需要启动一个ESB实例,主要负责广播消息,同时,监听消息队列,准备接收消息。
a、添加引用
创建控制台应用程序PublishSubscribe.Publish,然后使用NuGet,添加如下引用:
PublishSubscribe.Messages
Shuttle.Core.Data
Shuttle.Core.Domain
Shuttle.Core.Host
Shuttle.Core.Infrastructure
Shuttle.ESB.Core
Shuttle.ESB.Modules
Shuttle.ESB.SqlServer
b、配置文件app.config
配置文件中,主要配置了数据库连接字符串、以及Shuttle ESB的监听队列信息
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="serviceBus" type="Shuttle.ESB.Core.ServiceBusSection, Shuttle.ESB.Core"/>
<section name="sqlServer" type="Shuttle.ESB.SqlServer.SqlServerSection, Shuttle.ESB.SqlServer"/>
</configSections>
<appSettings>
<add key="SubscriptionManagerSecured" value="false"/>
</appSettings>
<connectionStrings>
<clear/>
<add name="SubscriptionConnection" connectionString="Uid=sa;Pwd=123456;Initial Catalog=shuttle;Data Source=172.22.51.180;Connect Timeout=900" providerName="System.Data.SqlClient"/>
</connectionStrings>
<sqlServer subscriptionManagerConnectionStringName="SubscriptionConnection"/>
<serviceBus>
<inbox
workQueueUri="msmq://./pubsub-publish-inbox-work"
deferredQueueUri="msmq://./pubsub-publish-inbox-deferred"
errorQueueUri="msmq://./shuttle-pubsub-error"/>
</serviceBus>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
c、控制台程序
在控制台程序Program中,主要经过如下几步,启动ESB服务总线实例:连接数据库、获取ESB的各种服务、设置ESB实例指定消息类型的监听队列、启动ESB实例、发送消息。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shuttle.Core.Data;
using Shuttle.ESB.SqlServer;
using PublishSubscribe.Messages;
using Shuttle.ESB.Core;
using Shuttle.Core.Infrastructure;
namespace PublishSubscribe.Publish
{
class Program
{
static void Main(string[] args)
{
//连接数据库
new ConnectionStringService().Approve();
//获取ESB的各种服务
var subscriptionManager = SubscriptionManager.Default();
//设置ESB实例监听队列类型
subscriptionManager.Subscribe(
new[]{
typeof(WorkDoneEvent).FullName
}
);
//启动ESB实例
var bus = ServiceBus.Create(c=>c.SubscriptionManager(subscriptionManager)).Start();
ColoredConsole.WriteLine(ConsoleColor.Green, "Server bus started. Press CTRL+C to stop.");
while (true) {
//控制台输入一个值
string s = Console.ReadLine();
//发送消息
var message = new OrderCompletedEvent
{
coment = s
};
bus.Publish(message);
Console.WriteLine("Published coment= {0}", message.coment);
}
}
}
}
d、一般处理程序
添加类:WorkDoneEventHandler。
该类集成需要继承IMessageHandler接口。它的作用主要是接收并处理接收到的消息。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shuttle.ESB.Core;
using Shuttle.Core.Infrastructure;
using PublishSubscribe.Messages;
namespace PublishSubscribe
{
public class WorkDoneEventHandler:IMessageHandler<WorkDoneEvent>
{
public void ProcessMessage(HandlerContext<WorkDoneEvent> context)
{
ColoredConsole.WriteLine(ConsoleColor.Blue, context.Message.Comment);
}
public bool IsReusable
{
get { return true; }
}
}
}
3、消息接收端A
消息接收端的配置与消息发送端的配置大同小异。因为它们都可以发送消息,也可以接收消息。只不过,由于消息接收端不一定是控制台程序,如果是类库呢?执行后就释放了,怎样接收消息呢(因为ESB实例启动后,程序运行着,才会监听到新收到的消息)?Shuttle ESB使用了通用主机的方式。
a、添加引用
创建类库项目PublishSubscribe.Subscriber1,并添加项目引用:
PublishSubscribe.Messages
Shuttle.Core.Data
Shuttle.Core.Domain
Shuttle.Core.Host
Shuttle.Core.Infrastructure
Shuttle.ESB.Core
Shuttle.ESB.Msmq
Shuttle.SqlServer
b、配置文件app.config
配置文件与消息发送端的配置基本类似,主要配置了数据库连接字符串、以及Shuttle ESB的监听队列信息。
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="serviceBus" type="Shuttle.ESB.Core.ServiceBusSection, Shuttle.ESB.Core"/>
</configSections>
<connectionStrings>
<clear/>
<add name="Subscription" connectionString="Uid=sa;Pwd=123456;Initial Catalog=shuttle;Data Source=172.22.51.180;Connect Timeout=900" providerName="System.Data.SqlClient"/>
</connectionStrings>
<serviceBus>
<inbox
workQueueUri="msmq://./pubsub-subscriber1-inbox-work"
errorQueueUri="msmq://./shuttle-pubsub-error"/>
</serviceBus>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
c、类库项目
这里的ServiceBus1Host类,需要继承Shuttle ESB的IHost接口,并实现它的Start方法。
这里,它需要实现的功能也完全类似于消息发送端:
连接数据库、获取ESB的各种服务、设置ESB实例指定消息类型的监听队列、启动ESB实例、加工处理消息。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shuttle.ESB.Core;
using Shuttle.Core.Data;
using Shuttle.ESB.SqlServer;
using PublishSubscribe.Messages;
using Shuttle.Core.Infrastructure;
using Shuttle.Core.Host;
namespace PublishSubscribe.Subscriber1
{
public class ServiceBus1Host:IHost,IDisposable
{
private IServiceBus bus;
public void Dispose()
{
bus.Dispose();
}
public void Start()
{
new ConnectionStringService().Approve();
var subscriptionManager = SubscriptionManager.Default();
subscriptionManager.Subscribe(
new[]{
typeof(OrderCompletedEvent).FullName
}
);
bus = ServiceBus.Create(c => c.SubscriptionManager(subscriptionManager)).Start();
ColoredConsole.WriteLine(ConsoleColor.Green, "Sub 1 started. Press CTRL+C to stop.");
}
}
}
d、一般处理程序
添加Subscriber1Handler类:
该类集成需要继承IMessageHandler接口。它的作用主要是接收消息发送端Pub的消息,经过处理后,并将消息重新发型消息。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shuttle.ESB.Core;
using PublishSubscribe.Messages;
using Shuttle.Core.Infrastructure;
namespace PublishSubscribe.Subscriber1
{
public class Subscriber1Handler : IMessageHandler<OrderCompletedEvent>
{
public void ProcessMessage(HandlerContext<OrderCompletedEvent> context)
{
string comment = string.Format("Subscriber1-----lzq: {0}", context.Message.coment);
ColoredConsole.WriteLine(ConsoleColor.Blue, comment);
//发送消息
context.Publish(
new WorkDoneEvent {
Comment = comment
}
);
}
public bool IsReusable
{
get { return true; }
}
}
}
e、修改启动方式
点击本项目的属性,在调试栏中选择“启动操作”,选择“启动外部应用程序”。选择本项目目录debug下的“Shuttle.Core.Host.exe”作为启动程序。例如我选择的目录如下:“D:\PublishSubscribe\PublishSubscribe.Subscriber1\bin\Debug\Shuttle.Core.Host.exe”
4、消息接收端B
完全仿制消息接收端A,编码消息接收端B。
然后修改解决方案的启动方式为多项目启动:将消息发送端和多个消息接收端均设置为启动项目。
5、结果演示
图1为服务端窗口,图2为两个客户端窗口。程序启动后,会出现下面三个控制台。在服务端输入“Hello World!”后,两个客户端(当然可以为更多个,不过太多客户端会影响接收效率)马上接收到消息。接收到后,它会给服务端返回一个消息,然后服务端又会收到两个客户端的消息。具体结果如图所示。
(图1)
(图2)
PS:这里的两个客户端就是两台终端显示机器,每个终端显示机器都可以有多个终端显示界面。所以,客户端1和客户端2往往是在不同的运行环境下的。