本文章是由機器翻譯。

Foundations

服務匯流排中的路由器

JUVAL LOWY

我我先前兩個資料行中所述.NET 的服務匯流排設計來解決核心問題 — 的網際網路連線能力及相關的安全性問題。 但是,服務匯流排可提供更多個只連線] 及 [訊息轉送。 此資料行開頭服務登錄,並再說明方面的路由器。 我 ’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 摘要,並將項目放置在左邊樹狀目錄中。 您可以瀏覽多個解決方案,並查看在右窗格中,因此您方案的管理頁面。 工具也會顯示可用的路由器和佇列,非常方便的管理它們。

定域機組為攔截器

服務匯流排確實一開始被開發來跨網路地址嚴重的連線問題的呼叫。 但是,它有更多的可能性。 比較使用服務匯流排架構基本的 Windows Communication Foundation (WCF) 架構。 在這兩種的情況下用戶端不會直接與服務,互動,但是相反的中介軟體會攔截呼叫]。 在一般的 WCF 的情況下中, 介軟體則 Proxy 解譯鏈結,導致服務,的圖 2 所示。

在轉接的呼叫的情況下中, 介軟體包含一般 WCF 中介軟體和服務匯流排本身,的圖 3] 所示。 從結構觀點來看,這是相同的設計,攔截呼叫,以提供額外的值。 在服務匯流排的目前版本,其他值會是能夠安裝路由器和佇列。 在就其實我相信服務匯流排很可能有功能強大的攔截器,而也會不一定其他方面會變為可用經過一段時間。


圖 2 攔截規則 WCF 呼叫


圖 3 的雲霧為攔截器

為路由器交界

服務匯流排,每個方案中的 URI 是實際的可定址的訊息連接。 用戶端可以將一個訊息傳送給該連接,並連接點上可以轉送到服務]。 但是,每個連接點也可以做為路由器。 多個服務可以訂閱到該的路由器,以及路由器可以轉送至所有的或只是單一的相同的用戶端訊息,的圖 4] 所示。

用戶端分離的路由器,服務且用戶端是擔心,就有甚至可能不路由器後面的任何服務。 用戶端需要知道的就是它需要傳送郵件到路由器所在的位置。 它信任與適當的通訊群組原則設定路由器。 因為該 indirectness 的訊息是原本一個方法,WCF 中就其實堅持由用戶端和訂閱服務繫結是單向的轉送繫結]。 用戶端有沒有方法或被 apprised 的服務端錯誤的接收服務的結果。 這即使繫結是一種方法不是大小寫。 如果用戶端則被多工處理多個服務中,然後根據定義從 (從哪一個服務嗎?) 作業傳回的結果或錯誤的服務嗎?) 的沒有意義。 也沒有回撥到其用戶端服務的方法。


圖 4 為路由器 的服務匯流排

路由器原則

方案管理員管理服務獨立的路由器。 每一個路由器必須要有一個原則來管理其行為和存留期 (Lifetime)。 現成的方案管理員必須執行以程式設計方式來建立和管理路由器的呼叫。 所有訊息的連接原則衍生自抽象類別 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 所示,它們在右邊的窗格中的屬性與互動。

必須要張貼郵件到路由器的用戶端是建立的 Proxy 的位址是路由器的位址,並呼叫 Proxy。

訂閱路由器

接收來自路由器訊息服務,它必須先訂到路由器將加入至主應用程式的端點的位址是路由器 ’s 位址。 執行這項操作的方法之一是使用 Helper 類別 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>


圖 7A 路由器服務匯流排總管


圖 8 訂閱路由器至路由器

使用路由器

路由器會有三個用途。 第一個是廣播到多個服務相同的訊息。 傳統的大小寫的就當然,發行事件訂閱服務匯流排視為一個事件集線器中介軟體的服務。 正如我 4 月問題中的欄中所提到 (請參閱 msdn.microsoft.com/magazine/dd569756.aspx) 還有多發佈非符合的眼睛,因此我 ’ll 延後到此資料行的結尾 discussing 事件的事件。

第二個使用路由器是為負載平衡器服務之間。 如果您設定到路由器的原則散佈到只有單一訂閱者的訊息,路由器作用中做為負載平衡器之間訂閱服務]。 所有負載平衡器會都遵循某些演算法決定哪個服務會處理下一個訊息。 常見的演算法包含循環配置資源]、 [隨機的有些公平排行榜] 和 [佇列。 我的測試表示服務匯流排使用 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,但也保持飛行日誌服務 A 中的所有呼叫的記錄)。 描述因此就目前的傳遞至所有 「 訂閱者 」 或單一訂閱者,路由器原則是不適當。 但是,您可以滿足需求很容易地使用兩個的路由器 圖 8 所示。

用戶端會傳遞郵件到最上層的路由器與所有服務的通訊群組的原則。 服務 A 訂閱在最上層的路由器。 最上層的路由器也訂閱 subrouter,以與單一訂閱者的通訊群組原則。 該 subrouter 訂閱服務 B 和 C 的服務。 圖 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 月討論)。 沒有任何的每個作業訂閱,因此訂閱者 (全部) 永遠接收所有的事件,即使它們不在乎 (即有一個相符的結束點) 的事件。

解決方案是不是要建立單一路由器處理所有事件,但若要建立每個事件路由器。 該事件有興趣的服務會訂閱該事件 ’s 特定路由器。 就為與事件繫結,讓每個事件路由器會經由有管理每項操作,主控件執行個體的每個訂閱者,因為所有的路由器會監視一個單一主機將不來篩選事件。 訂閱者必須開啟和關閉個別主應用程式訂閱或取消訂閱事件。 這需要繁瑣和重複的程式碼。

好消息是的協助程式類別 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);
}

兩個類別子,EventsRelayHost 和 EventsRouterHost,定義在 EventsHost 圖 10 的實作中,並且 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 執行大量 lifting 管理主機的事實。 EventsRouterHost 和 EventsRelayHost 只是覆寫繫結的管理方法。

以外的使用 EventsRouterHost 就像使用的 EventsRelayHost,訂閱和取消訂閱,而非開啟和關閉主應用程式運作:

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

先前的程式碼會開啟並關閉個別的主機內部的監視該事件。

建立事件路由器

EventsRouterHost 需要解決方案系統管理員事先,設定路由器,它不會嘗試建立路由器。 所做的選擇刻意分隔設定在路由器和訂閱者從其原則。 所有用戶端看到是連接 ’s 地址,,若要引發事件,透過單向轉送繫結,您可以使用相同的 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 摘要。

路由器與。 事件繫結

對於大部分的定域機組為基礎的情況下發行-訂閱方案,我會建議事件繫結。 第一次,找限制為嚴重的讓分的訂閱者的最大數目。 很可能會超過適合您的應用程式 50。 不過,如果經過一段時間您需要支援多個 「 訂閱者 」? 沒有這類限制頭上繫結事件。 其他問題的路由器的方法是需要事先建立路由器,並擔心租用支持。 事件繫結,另一方面的函式為沒有這些負債粗糙的臨機操作路由器。

有兩個儲存 graces 路由器為主的事件管理。 第一個是系統管理員使用例如服務匯流排瀏覽器的工具來管理路由器,甚至間接管理在 「 訂閱者 」 的選項。 第二個和多個 ubstantial,是能夠與佇列建立佇列的發行者和佇列的訂閱者合併路由器我將說明在我的下一個資料行中的方法。

Juval Lowy 是 IDesign 的軟體架構設計師,提供 WCF 訓練及架構諮詢。 他最新的活頁簿是 「 程式設計 WCF 服務,第二版 」 (O’Reilly 2008)。 他也是矽谷的 [Microsoft 地區主管。 在 idesign.net,請連絡 Juval。