Foundation

服务总线中的路由器

JUVAL LOWY

我以前的两个列中我将介绍.NET 服务总线旨在解决核心问题) 的 Internet 连接和相关的安全问题。 但是,服务总线提供了更多个单纯的连接和邮件中继。 此列与服务注册表启动,然后描述的路由器的各个方面。 我 ’ll 于某队列群中的下一专栏。 在这两个的列中我将介绍新功能强大的设计模式,如何合并路由器和队列和我的帮助器类和工具来简化了总体体验。

服务注册表

在.NET 服务总线提供了解决方案的基本地址或其 sub-URIs 的任何一个上侦听的服务的一个 ATOM 源。 使用浏览器,您可以通过导航到该解决方案 (或 URI) 中查看源。 源可作为一个注册表和目录服务和解决方案中 (如路由器和队列) 的方面。

默认情况下, 您的服务不在浏览器中可见。 您可以通过使用如下定义,ServiceRegistrySettings 终结点行为类控制服务注册表发布:

public enum DiscoveryType
{
Public,
Private
}
public class ServiceRegistrySettings : IEndpointBehavior
{
public ServiceRegistrySettings();
public ServiceRegistrySettings(DiscoveryType discoveryType);
public DiscoveryType DiscoveryMode
{get;set;}
public string DisplayName
{get;set;}
}

主机需要将该行为以编程方式添加到每个终结点,您要发布到注册表。 (有是没有匹配的可配置的行为。渚嬪的方式  向注册表发布的所有终结点,可以使用代码如下所示:

IEndpointBehavior registeryBehavior =
new ServiceRegistrySettings(DiscoveryType.Public);
ServiceHost host = new ServiceHost(typeof(MyService));
foreach(ServiceEndpoint endpoint in host.Description.Endpoints)
{
endpoint.Behaviors.Add(registeryBehavior);
}
host.Open();


图 1 中的服务总线资源管理器

若要帮助可视化服务总线,我开发的图 1 所示服务总线管理器。 工具接受名称浏览,解决方案和登录到该数据源后,将显示为您正在运行的服务。 在资源管理器不会是分析 ATOM 源并将项目放入左侧树。 可以浏览多个解决方案,在右窗格中看到您的解决方案管理页。 该工具还显示了可用的路由器和队列,并在管理这些方面非常方便。

为侦听器的群

服务总线是确实最初开发跨 Web 地址的调用,锐音符的连接问题。 但是,它具有更多的可能性。 比较有服务总线体系结构基本的 Windows 通信基础 (WCF) 体系结构。 在这两种情况下,客户端不直接与服务,交互,但而调用被截获的中间件。 在常规 WCF,在中间件是代理并解释链导致服务,的图 2 所示。

在中继调用的情况下,中间件普通的 WCF 中间件和包含本身,服务总线的图 3 所示。 从体系结构角度来说,这是相同的设计,截获调用提供其他值。 中殑服务总线当前鐗堟湰鐨勫叾浠栦该值功能是安装路由器和队列可以。 实际上,我认为服务总线很可能的功能强大的截听器,并且毫无疑问其他方面将变为可用时间。


图 2 截取正常 WCF 调用


图 3 云作为侦听器

鑱旀帴作为路由器

中在服务总线解决方案中的每个 URI 是实际的可寻址的消息连接。 客户端可以向消息发送到该连接,以及交接点可以将它中继到服务。 但是,每个连接可以还用作路由器。 路由器,可以订阅多个服务并的图 4 所示,路由器可以将转发到它们的所有或只是一个其中,相同的客户端消息。

从后面路由器,服务分离的客户端,据客户端是考虑,有可能会不甚至路由器后面的任何服务。 客户端需要知道的就是需要将消息发送到路由器的位置。 它信任路由器将使用相应的通讯组策略进行配置。 因该 indirectness,邮件本身一个方法,实际上,WCF 坚持使用客户端和订阅的服务,绑定是单向中继绑定。 客户端具有无法接收结果从服务或正在 apprised 服务端错误。 这是这种情况即使绑定没有一种方法。 如果客户端消息被多路复用将多个服务,定义则没有意义 (从哪个的服务吗?) 该操作从返回的结果或错误的服务吗?)。 此外没有为回调到其客户端服务的方法。


图 4 作为路由器 的服务总线

路由器策略

解决方案管理员管理服务独立的路由器。 每个路由器必须具有一个策略来控制其行为和生存期。 ,超出解决方案管理员必须执行创建和管理路由器以编程方式调用。 所有邮件的联接策略从抽象类派生 JunctionPolicy,的图 5 所示。

发现连接属性是 DiscoverabilityPolicy 类型的枚举。 它控制解决方案的交接点是否包含在该 ATOM 源。 发现默认 DiscoverabilityPolicy.Managers,这意味着一个专用的策略。 将其设置为 DiscoverabilityPolicy.Public,将它发布到源中。 ExpirationInstant 属性控制的交接点生命周期中。 该策略过期后将删除交接点。 一天的 ExpirationInstant 默认值。 显然的可能也可能不适合您的路由器 (或队列),因此,通常需要将它设置为值,它监视。 交接点是过期,如果交接点仍在使用时才应该续订它。 我调用的续订 “ 租约时间 ” 的交接点,和调用方的扩展 “ 发起 ” 租约。最后,TransportProtection 属性控制交接点,邮件传输的安全性。 当发布或检索原始的 WCF 消息或从该邮件鑱旀帴,您限于传输安全使用 TransportProtectionPolicy.AllPaths 的值。 传输安全所有的联接策略的默认值,建议您永远不会将其设置其他任何值。

图 5 的 JunctionPolicy 类

public enum DiscoverabilityPolicy
{
Managers,Public //More values
}
public enum TransportProtectionPolicy
{
None,AllPaths
}
[DataContract]
public abstract class JunctionPolicy
{
public JunctionPolicy();
public JunctionPolicy(JunctionPolicy policyToCopy);
public DateTime ExpirationInstant
{get;set;}
public DiscoverabilityPolicy Discoverability
{get;set;}
public TransportProtectionPolicy TransportProtection
{get;set;}
//More members
}

图 6 的 RouterPolicy 类

public enum MessageDistributionPolicy
{
AllSubscribers,
OneSubscriber
}
[DataContract]
public class RouterPolicy : JunctionPolicy,...
{
public RouterPolicy();
public RouterPolicy(RouterPolicy otherRouterPolicy);
public int MaxSubscribers
{get;set;}
public MessageDistributionPolicy MessageDistribution
{get;set;}
//More members
}

与在的图 6 中定义类 RouterPolicy,表示每个路由器的策略。 这两个主要属性此处是 MaxSubscribers 和 MessageDistribution。 由于隐含的名称 MaxSubscribers 将是最大并发允许连接到路由器的订阅者数。 默认值为 1,,最大值是 50。 一旦路由器 maxed 外,尝试订阅的其他服务出现错误。 MessageDistribution MessageDistributionPolicy 类型的枚举,默认为 MessageDistributionPolicy.OneSubscriber--只有一个订阅服务获取该消息。 将其设置为 MessageDistributionPolicy.AllSubscribers 将邮件传递到所有的服务。

管理路由器策略

您可以使用 RouterManagementClient 类,示,管理您的路由器策略:

public static class RouterManagementClient
{
public static RouterClient CreateRouter(
TransportClientEndpointBehavior credential,
Uri routerUri,RouterPolicy policy);
public static void DeleteRouter(
TransportClientEndpointBehavior credential,
Uri routerUri);
public static RouterClient GetRouter(
TransportClientEndpointBehavior credential,
Uri routerUri);
public static RouterPolicy GetRouterPolicy(
TransportClientEndpointBehavior credential,
Uri routerUri);
public static DateTime RenewRouter(
TransportClientEndpointBehavior credential,
Uri routerUri,TimeSpan requestedExpiration);
}

RouterManagementClient 是一个静态的类,它的所有方法都需要凭据对象 (类型 TransportClientEndpointBehavior,我以前的专栏中讨论的 (请参阅 msdn.microsoft.com/magazine/dd942847.aspx)。 下面的代码演示创建一个简单的路由器和策略:

Uri routerAddress =
new Uri(@"sb://MySolution.servicebus.windows.net/MyRouter/");
TransportClientEndpointBehavior credential = ...;
RouterPolicy policy = new RouterPolicy();
policy.ExpirationInstant = DateTime.UtcNow + TimeSpan.FromMinutes(5);
policy.MaxSubscribers = 4;
policy.MessageDistribution = MessageDistributionPolicy.AllSubscribers;
RouterManagementClient.CreateRouter(credential,routerAddress,policy);

在示例中,您可以实例化一个路由器的策略对象,设置策略,如分发订阅服务的所有消息和限制路由器不超过四个订阅服务器的某些值。 路由器配置为具有短只有 5 分钟的生命周期。 所有所需安装路由器是 RouterManagementClient 的 CreateRouter 使用调用方法策略和一些有效的凭据。

以编程方式调用除了,使用我服务总线资源管理器可以查看和修改路由器。 您可以通过指定其地址和各种策略属性创建一个新的路由器。 很多相同的方式,您可以删除该解决方案中的所有路由器。

您还可以查看和修改现有的路由器的策略,通过在解决方案树中选择并交互它们在右边的窗格中的属性,如图 7 的中所示。

所有客户端需要向路由器投递邮件执行的操作是创建的代理的地址是路由器的地址,并调用该代理。

订阅路由器

一个嶅姟从路由器接收消息它必须先订阅路由器通过向主机终结点的地址是路由器已经地址。 执行此操作的一种方法是使用帮助器类 RouterClient,已定义,如下所示:

public sealed class RouterClient
{
public ServiceEndpoint AddRouterServiceEndpoint<T>(
ServiceHost serviceHost);
//More members
}

创建或获得路由器 RouterManagementClient 的方法返回 RouterClient 一个的实例。 您需要调用该 AddRouterServiceEndpoint < T >与您服务的主机的实例的方法。 您也只是可以添加终结点到主机 (以编程方式或在一个配置文件) 的地址是路由器的地址。 示例如下:

<service name = "MyService">
<endpoint
address = "sb://MySolution.servicebus.windows.net/MyRouter/"
binding = "netOnewayRelayBinding"
contract = "..."
/>
</service>


图 7 A 服务总线资源管理器中的路由器


图 8 订阅路由器到路由器

使用路由器

路由器有三个使用。 第一个是广播到多个服务相同的消息。 典型情况,当然,发布到订阅视为一个事件的集线器中间件服务总线的服务的事件。 我在 4 月的专栏中所述 (请参阅 msdn.microsoft.com/magazine/dd569756.aspx) 不存在多个发布比满足在的眼睛,以便我 ’ll 推迟到本专栏的结尾讨论事件的事件。

使用第二个路由器是负载平衡器服务之间。 如果您将路由器策略配置为分发到单个订阅服务器的邮件,路由器实际上作为订阅服务之间负载平衡器。 所有负载平衡器都按照一些算法确定哪个服务将处理下一条消息。 常见的算法包括循环、 随机的一些合理的记分卡和队列。 我测试指示服务总线使用一个 pseudo–round 循环也就是很公平的循环复用时间的一些 95%。 为简化创建一个负载平衡路由器,还我帮助器类 ServiceBusHelper 提供了许多重载如下所示的 CreateLoadBalancingRouter 方法:

public static partial class ServiceBusHelper
{
//Uses CardSpace for credential
public static void CreateLoadBalancingRouter(string balancerAddress);
/* Additional CreateLoadBalancingRouter(...)
with different credentials */
}

CreateLoadBalancingRouter 分布单个订阅服务器将邮件的策略创建一个路由器。 它还检测路由器是否存在之前创建它并自动续订路由器的租约。 相应的文章代码下载中包括 CreateLoadBalancing 路由器的实现。

所有公共 CreateLoadBalancingRouter 方法采用不同的凭据类型 (提供该实现使用 CardSpace),构造 TransportClientEndpointBehavior,和内部重载 CreateLoadBalancingRouter 调用的实例。 该重载的版本创建也将路由器发布到新闻复制在 ATOM 负载平衡策略,并使用指定的策略和凭据调用 CreateRouter。 CreateRouter 首先检查是否已创建路由器使用 RouterExists 方法。 遗憾的是,检查唯一的方法是查看是否在您试图获取路由器的策略时出现错误。

如果路由器存在,CreateRouter 返回其相应 RouterClient。 濡傛灉路由器涓嶅瓨鍦  CreateRouter 第一次创建一个计时器来监视的租约,到期 Lambda 表达式用作主办方。 当租约时间接近其结束,计时器调用 Lambda 表达式以续订该租约。 然后,CreateRouter 创建路由器。

路由器到路由器

第三个用于路由器是消息路由到其他鑱旀帴,如其他路由器 (或队列)。 若要订阅到另一个路由器,使用 SubscribeToRouter 方法 RouterClient,如下所示的:

public sealed class RouterClient
{
public RouterSubscriptionClient SubscribeToRouter(
RouterClient routerClient,
TimeSpan requestedTimeout);
//More members
}
[Serializable]
public class RouterSubscriptionClient
{
public DateTime Expires {get;}
public DateTime Renew(TimeSpan requestedExpiration,
TransportClientEndpointBehavior credential);
public void Unsubscribe(TransportClientEndpointBehavior credential);
//More members
}

SubscribeToRouter 接受您希望订阅,返回的 RouterSubscriptionClient 实例路由器的路由器客户端。 主 RouterSubscriptionClient 的使用是取消订阅并续订订阅租约。

渚嬪的方式  有服务 A、 服务 B,和服务 C。 您需要的每个客户端调用始终指向服务 A 和 B 的服务或服务 c。 (设想一下需要加载平衡服务 B 和 C 的服务,但还记录了所有调用 logbook 服务 A 中)。 到目前为止,描述传递到所有订阅服务器或在单个订阅服务器的路由器策略是不足。 但是,您可以满足要求很容易地通过使用两个的路由器图 8 中所示。

客户端传递到顶级路由器使用的分发到所有的服务策略的邮件。 服务 A 订阅顶级的路由器。 使用单个订阅服务器的通讯组策略的一个 subrouter 还订阅顶级的路由器。 客户服务 B 和服务 C 的 subrouter 订阅。 图 9 说明所需的路由器配置的以及将订阅该 subrouter 到顶部路由器的如何。

订阅订阅路由器服务时,没有有关不同于顶级路由器订阅的订阅的一个重要细节。 服务终结点必须表示它是愿意接收来自订阅路由器,即使邮件发往顶级的路由器。 这表示通过订阅服务将侦听的 URI 属性,终结点的设置时遇到本身地址顶级路由器服务终结点。 在图 9 中,使用地址地址两个服务 B,服务 C 将不得不定义它们的终结点如下所示:

<service name = "MyService">
<endpoint listenUri =
"sb://MySolution.servicebus.windows.net/MySubRouter/"
address = "sb://MySolution.servicebus.windows.net/MyTopRouter/"
binding = "netOnewayRelayBinding"
contract = "..."
/>
</service>

可以自动进行此设置,扩展名为以下服务的主机文件:

public static partial class ServiceBusHelper
{
public static void SubscribeToRouter(this ServiceHost host,
string subRouterAddress)
{
for(int index = 0;
index < host.Description.Endpoints.Count;index++)
{
host.Description.Endpoints[index].ListenUri =
new Uri(subRouterAddress);
}
}
//More members
}

假设路由器可以订阅多个路由器,应非常注意哪个地址--也就是他们想要接收邮件的顶级路由器--的订阅在订阅的路由器的服务。

请注意,删除顶级的路由器或终止订阅剪切关闭该 subrouters 从客户端的邮件。 这,反过来,是一个功能的解决方案管理员,可以使用此步骤中有效地关闭服务,而不关闭其主机下的排序。

图 9 订阅路由器到路由器

Uri topRouterAddress =
new Uri(@”sb://MySolution.servicebus.windows.net/MyTopRouter/");
Uri subRouterAddress =
new Uri(@”sb://MySolution.servicebus.windows.net/MySubRouter/");
TransportClientEndpointBehavior credential = ...;
RouterPolicy topRouterPolicy = new RouterPolicy();
subRouterPolicy.MaxSubscribers = 20;
topRouterPolicy.MessageDistribution =
MessageDistributionPolicy.AllSubscribers;
RouterClient topRouterClient = RouterManagementClient.CreateRouter(
credential,topRouterAddress,topRouterPolicy);
RouterPolicy subRouterPolicy = new RouterPolicy();
subRouterPolicy.MaxSubscribers = 30;
subRouterPolicy.MessageDistribution =
MessageDistributionPolicy.OneSubscriber;
RouterClient subRouterClient = RouterManagementClient.CreateRouter(
credential,subRouterAddress,subRouterPolicy);
RouterSubscriptionClient subscription = subRouterClient.
SubscribeToRouter(topRouterClient,
TimeSpan.MaxValue);
//Sometime later
subscription.Unsubscribe(credential);

使用路由器的事件

我提到消息服务总线的交接点可以作为一个粗糙的事件的中心并广播邮件订阅服务。 您需要安装一个路由器策略,以便最大化的订阅者数,并通知所有订阅服务器。 因此,这样做的缺点是类似于使用该事件绑定 (在我在 msdn.microsoft.com/magazine/dd569756.aspx 4 的专栏中讨论) 的。 没有每个操作的订阅和订阅服务器 (全部) 始终因此接收所有的事件,甚至它们执行不关心 (那些只是有一个匹配的终结点的) 的事件。

该解决方案不是创建单个路由器处理的所有事件,但要创建一个路由器,每个事件。 该事件已经特定路由器将订阅服务的兴趣只在该事件。 为与绑定,事件有一个路由器,每个事件的实现监视所有的路由器的单个主机将不执行任何要筛选事件的操作,因为具有管理每个操作,主机实例的每个订阅服务器。 订阅服务器需要打开和关闭相应主机订阅或取消对该事件。 这要求乏味和重复代码。

好消息是 EventsRelayHost 我在我以前的专栏中显示的帮助器类所执行的已操作只是的与公共基类称为 EventsHost 的一些重构,也可以是用于路由器而不是事件中继绑定适用。 (对于路由器,您需要只是单向的中继绑定。

第一次,我定义抽象基类 EventsHost,如下所示:

//Partial listing
public abstract class EventsHost
{
public EventsHost(Type serviceType,string baseAddress);
public EventsHost(Type serviceType,string[] baseAddresses);
public abstract void SetBinding(NetOnewayRelayBinding binding);
public abstract void SetBinding(string bindingConfigName)
protected abstract NetOnewayRelayBinding GetBinding();
public void Subscribe();
public void Subscribe(Type contractType);
public void Subscribe(Type contractType,string operation)
public void Unsubscribe();
public void Unsubscribe(Type contractType);
public void Unsubscribe(Type contractType,string operation);
}

图 10.The 实现 EventsHost 中定义两个子类 EventsRelayHost 和 EventsRouterHost,并且 EventsRelayHost 显示在我以前的专栏中。

图 10 定义 EventsRelayHost

//For use with the events binding, see previous column
public class EventsRelayHost : EventsHost
{...}
public class EventsRouterHost : EventsHost
{
public override void SetBinding(NetOnewayRelayBinding binding)
{
RelayBinding = binding;
}
public override void SetBinding(string bindingConfigName)
{
SetBinding(new NetOnewayRelayBinding(bindingConfigName));
}
protected override NetOnewayRelayBinding GetBinding()
{
return RelayBinding ?? new NetOnewayRelayBinding();
}
}

EventsRouterHost 类似于 EventsRelayHost--两个管理每个事件,主机,它的重要不如果地址他们监视的事件的终结点的地址或路由器。 唯一的区别是在 EventsRouterHost 必须使用单向中继绑定到接收该事件,相对于由 EventsRelayHost 绑定事件。 非,EventsRouterHost 和 EventsRelayHost 应相同。 这将反映在这一事实两个派生 EventsHost,不会管理该主机的粗工作中。 EventsRouterHost 和 EventsRelayHost 只是重写绑定管理方法。

以外的使用 EventsRouterHost 就像使用的 EventsRelayHost,订阅和取消订阅函数而不是打开和关闭宿主:

EventsHost host = new EventsRouterHost(typeof(MyService),
"sb://MySolution.servicebus.windows.net/...");
host.Subscribe();
...
host.Unsubscribe(typeof(IMyEvents),"OnEvent2");

上面的代码将打开并关闭内部的只是该事件的监视各自主机。

创建事件路由器

EventsRouterHost 需要解决方案管理员预先,配置路由器,它将不尝试创建路由器。 所做的选择故意单独配置路由器和订阅服务器从其策略。 客户端看到的就是交接点已经地址,并且若要通过单向中继绑定激发事件,您可以发布事件中使用在同一 EventRelayClientBase 我以前的专栏中介绍。

为简化的创建事件路由器,可用于我 ServiceBusHelper 创建路由器:

public static partial class ServiceBusHelper
{
//Uses CardSpace for credential
public static RouterClient[] CreateEventRouters(string baseAddress,
Type contractType);
/* Additional CreateEventRouters(...) with different credentials */
}

CreateEventRouters 接受路由器的基址。 它约定名称和操作名称追加到基本地址,并打开在地址下的各个路由器。 渚嬪的方式  考虑到以下的基址:

sb://MySolution.servicebus.windows.net/

然后此约定定义:

[ServiceContract]
interface IMyEvents
{
[OperationContract(IsOneWay = true)]
void OnEvent1();
[OperationContract(IsOneWay = true)]
void OnEvent2(int number);
[OperationContract(IsOneWay = true)]
void OnEvent3(int number,string text);
}

CreateEventRouters 创建下列路由器:

sb://MySolution.servicebus.windows.net/IMyEvents/OnEvent1/
sb://MySolution.servicebus.windows.net/IMyEvents/OnEvent2/
sb://MySolution.servicebus.windows.net/IMyEvents/OnEvent3/

这正是 EventsRouterHost 的需要。 本文的代码下载提供 ServiceBusHelper.CreateEventRouters,错误处理和几个重载的安全方法的情况下实现的部分列表。

在没有凭据的公用 CreateEventRouters 默认使用 CardSpace,通过创建 TransportClientEndpointBehavior 对象并将它传递给内部方法。 该方法使用反射来获取服务约定类型上操作的集合。 每个操作,它将调用 CreateEventRouter 方法,传递路由器该策略,该地址。 对于每个路由器策略 maxes 的订阅服务器的数量、 提供所有这些事件并将路由器至在 ATOM 源。

路由器与。 事件绑定

对于大多数情况下的基于云的发布-订阅解决方案,建议事件绑定。 第一次,找限制到订阅服务器是一个严重 handicap 的最大数量。 有可能将为您的应用程序的足够多个 50。 但是,如果随着时间的推移需要支持多个订阅服务器吗? 没有这些限制在绑定事件。 与路由器方法的其他问题是需要预先创建路由器和担心租约主办关系。 事件绑定,另一方面,为没有这些负债粗糙的特殊的路由器的功能。

有两个保存 graces,基于路由器的事件管理。 第一个是的系统管理员来管理该订阅服务器,甚至间接管理路由器中使用工具 (如服务总线资源管理器选项。 第二个和多个 ubstantial,是能够路由器结合排队的发行商和队列的订阅者创建的队列,我将在下一专栏中介绍的方法。

Juval Lowy 是 IDesign 的一名软件架构师,提供 WCF 培训和 WCF 体系结构咨询服务。 他最新简介册是 “ 编程 WCF 服务,第二版 ” (O’Reilly,2008年)。 他也是在硅谷的 Microsoft 区域主任。 在 idesign.net 联系 Juval。