上一篇文章中,介绍了Shuttle ESB架构模型中的三个重要部分。今天,我们继续介绍剩余的三个内容:模式和消息路由。
四、模式
Request/Response(请求/响应模式)
对基于Request/Response消息机制的内容,你可以看WiKi的一些文章:http://en.wikipedia.org/wiki/Request-response
向一个终端发送请求,执行某项功能,你可以发送一个命令消息:
bus.Send(new RequestMessage());
虽然这是一个非常简单的模式,但是,它却构成了一种耦合紧密的行为。尽管如此,这也未必是一件坏事,在许多情况下这是绝必需的。
通常,命令消息的Handler(处理程序),进行业务逻辑与消息的处理。但是很多时候,请求需要有响应。
响应可以以命令消息或者事件消息的形式返回。使用的时候非常简单,你只需要通过服务总线实例进行如下调用:
bus.Send(new ResponseMessage(), c => c.Reply());
当然,只要你愿意,你可以将“响应”作为事件消息返回,来达到解耦合的目的。那么这将不再是请求/响应模式了,而是发布订阅模式。
请求/响应模式的好处是:它提供了一种机制,使调用者发出调用请求后,能够收到服务端的反应。
Publish/Subscribe(发布订阅模式)
对于基础发布订阅消息模式的更多内容,你可以看这里:http://en.wikipedia.org/wiki/Publish/subscribe
这种模式的特点就是,让发布者和订阅者之间没有行为耦合。实际上,一个事件消息可能都没有订阅者。但是这是不太符合实际情况的。太多的实践证明,大多数情况下,我们要求至少要有一个订阅者。
发布一个事件消息,你可以采用如下格式:
bus.Send(new ResponseMessage(), c => c.Reply());
消息发送后,每一个订阅者会都到接收到自己的消息。这完全不同于一对一的消息分配处理机制。
消息分发
可想而知,如果一个端点接收太多的消息,那么处理这些消息就会端点处理能力降低,而且变得臃肿不堪。这种情况下,可以将消息改分配到服务节点上。
如果该终端接收到一个服务节点的请求消息,它将向其他服务节点自动分配该消息。一个终端可以配置成只发送消息。配置很简单,只需要设置收件箱标签分配属性为true即可。
由于消息分布都集成到收件箱,在处理相同端点时,只需要在多个不同的机器上安装一对一的消息。你接收消息的一端,需要一个控制收件箱的配置。因为所有的Shuttle消息都需要处理,而不是在队列中保持等待状态。
每一个服务站点在配置中唯一标识,端点的控制收件箱需要如下配置:
<configuration> <configSections> <section name="serviceBus" type="Shuttle.ESB.Core.ServiceBusSection, Shuttle.ESB.Core"/> </configSections> <serviceBus> <control workQueueUri="msmq://./control-inbox-work" errorQueueUri="msmq://./shuttle-error"/> <inbox distribute="true" workQueueUri="msmq://./inbox-work" errorQueueUri="msmq://./shuttle-error"/> </serviceBus> </configuration>
任何接收消息的一端都能这样配置。
然后,你就可以根据你的需要,建立多个服务节点。随着相关的所有发布者的增多,就会形成一个消息的逻辑终点。发布者的配置如下:
<configuration> <configSections> <section name="serviceBus" type="Shuttle.ESB.Core.ServiceBusSection, Shuttle.ESB.Core"/> </configSections> <serviceBus> <worker distributorControlWorkQueueUri="msmq:///control-inbox=work" /> <inbox workQueueUri="msmq://./workerN-inbox-work" errorQueueUri="msmq://./shuttle-error" threadCount="15"> </inbox> </serviceBus> </configuration>
只要应用程序配置文件包含一个闲置线程的标记,它就会发送一个消息给发布者,表明一个线程成为可执行的。然后订阅者将为每个可用的线程发布消息。
当应用程序配置文件包含工人标记每个线程去闲置将消息发送到经销商表示,一个线程已成为可执行的话。经销商将为每个可用的线程发送消息。
消息分发的特例
一些队列不需要消息分发。不使用客户终端,而是用它的另一个实例,也可以使用相同的输入队列。这种机制适用于代理。因为代理通过消费者线程运行的消耗,集中管理消息。因为消费者来自哪里都无所谓,所以队列能够给各个线程使用。
像基于MSMQ或者基于SqlServer的队列,这些都是通过启一个线程运行host,使用Handler进行消息处理。代理的方式不同于这种方式。在代理方式中,过程A将不知道这些消息所消耗的过程,而且导致B过程可能向其他端点获取消息。
五、消息路由
通常,我们说发送一个消息。根据“发送”,我们就确定它是一个命令消息。但是,它不一定必须是命令消息。你也可以给一个特定端点发送一个事件消息。实际情况下,更多的往往是发送事件消息,而不是命令消息。消息发送后,通过调用服务总线实例相关的重载方法:
TransportMessage Send(object message); TransportMessage Send(object message, Action<TransportMessageConfigurator> configure);
只有那些没有RecipientInboxWorkQueueUri集的信息,将会通过服务总线进行传输。如果你需要访问任何可用的信息源数据,传输信息的Envelop将被退回。Shuttle ESB采用了ImessageRouteProvider的实现,来确认消息发送。
public interface IMessageRouteProvider { IEnumerable<string> GetRouteUris(object message); } The message route provider to use is specified when constructing the service bus: bus = ServiceBus .Create(c => c.MessageRouteProvider(new DefaultForwardingRouteProvider()) .Start();
默认消息路由提供者,使用应用配置文件,来确定往哪发送消息:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="serviceBus" type="Shuttle.ESB.Core.ServiceBusSection, Shuttle.ESB.Core"/> </configSections> <serviceBus> <messageRoutes> <messageRoute uri="msmq://serverA/inbox"> <add specification="StartsWith" value="Shuttle.Messages1" /> <add specification="StartsWith" value="Shuttle.Messages2" /> </messageRoute> <messageRoute uri="sql://serverB/inbox"> <add specification="TypeList" value="DoSomethingCommand, Assembly" /> </messageRoute> <messageRoute uri="msmq://serverC/inbox"> <add specification="Regex" value=".+[Cc]ommand.+" /> </messageRoute> <messageRoute uri="sql://serverD/inbox"> <add specification="Assembly" value="TheAssemblyName" /> </messageRoute> </messageRoutes> </serviceBus> </configuration>
IMessageRouteProvider接口的每一个实现,都能决定一个实现线路。然而,它都需要从给定的消息发送。一个典型的场景,以及defaultmessagerouteprovider的工作方式,是使用完整的类型名称来确定目标。
注意:使用发送的每个消息类型,只能被发送到一个端点。
原文地址:http://shuttle.github.io/shuttle-esb/architecture/#Concepts