Service Bus 佇列、主題和訂閱
新版的 Windows Azure Service Bus 新增了一組以雲端為基礎、訊息導向中介軟體技術,包含可靠訊息佇列和長期發行/訂閱訊息。您可將這些「代理」訊息功能視為非同步或解除解合的訊息功能,這些功能可支援採用 Service Bus 訊息網狀架構的發行訂閱、時間解除結合及負載平衡案例。解除結合的通訊有許多好處;例如,用戶端和伺服器可以非同步方式連線 (視需要) 及執行其作業。
Service Bus 中新代理訊息功能的核心是由以下三種訊息模式所構成:佇列、主題/訂閱和規則/動作。
佇列
佇列提供對一或多個競爭消費者的先進先出 (FIFO) 訊息傳遞。亦即,一般是預期接收者依訊息加入佇列的時間順序來接收和處理訊息,而且每則訊息只能由一個訊息消費者接收和處理。使用佇列的主要優點是達成應用程式元件的「時間解除結合」。換句話說,因為訊息是長期儲存在佇列中,所以產生者 (傳送者) 和消費者 (接收者) 不需要同時傳送和接收訊息。此外,產生者不必等待消費者的回覆,即可繼續處理及傳送訊息。
相關優點是「負載調節」,讓產生者和消費者可以用不同速率傳送和接收訊息。在許多應用程式中,系統負載會隨著時間改變;但是,每一單位的工作所需的處理時間通常不變。利用佇列來調解訊息產生者和消費者,這表示只需要佈建消費性應用程式,即可處理平均負載而非高峰負載。佇列的深度會一直增加,且合約會隨著連入負載而改變。這可直接節省為應用程式負載提供服務所需的基礎結構數量相關費用。隨著負載增加,可以增加更多背景工作處理程序,以便從佇列讀取。每則訊息都是由其中一個背景工作處理程序所處理。此外,即使背景工作電腦的處理能力各有不同 (因為它們會以各自的最大速率提取訊息),此種提取式負載平衡還是能充分利用背景工作電腦。此模式通常稱為「競爭消費者」模式。
使用佇列來調解訊息產生者和消費者,可提供元件之間的固有鬆散結合。因為產生者和消費者並未注意到彼此,所以消費者可以在不對產生者造成影響的情況下升級。
建立佇列是一個多步驟的處理程序。Service Bus 訊息實體 (佇列和主題) 的管理作業是透過 NamespaceManager 類別來執行,而此類別的架構方式為提供 Service Bus 命名空間和使用者認證的基底位址。NamespaceManager 會提供用來建立、列舉和刪除訊息實體的方法。根據簽發者名稱和共用金鑰建立 TokenProvider 物件以及 服務命名空間 管理物件之後,您即可使用 CreateQueue 方法來建立佇列。例如:
// Create management credentials TokenProvider credentials = TokenProvider.CreateSharedSecretTokenProvider(IssuerName, IssuerKey); // Create namespace client namespaceManager namespaceClient = new namespaceManager(ServiceBusEnvironment.CreateServiceUri("sb", ServiceNamespace, string.Empty), credentials);
您可接著以 Service Bus URI 作為引數,建立佇列物件和訊息 Factory。例如:
QueueDescription myQueue; myQueue = namespaceClient.CreateQueue("TestQueue"); MessagingFactory factory = MessagingFactory.Create(ServiceBusEnvironment.CreateServiceUri("sb", ServiceNamespace, string.Empty), credentials); QueueClient myQueueClient = factory.CreateQueueClient("TestQueue");
您可接著將訊息傳送到佇列。例如,若有名為 MessageList 的代理訊息清單,則會出現類似下面的程式碼:
for (int count = 0; count < 6; count++) { var issue = MessageList[count]; issue.Label = issue.Properties["IssueTitle"].ToString(); myQueueClient.Send(issue); }
您可以從佇列接收訊息,如下所示:
while ((message = myQueueClient.Receive(new TimeSpan(hours: 0, minutes: 0, seconds: 5))) != null) { Console.WriteLine(string.Format("Message received: {0}, {1}, {2}", message.SequenceNumber, message.Label, message.MessageId)); message.Complete(); Console.WriteLine("Processing message (sleeping...)"); Thread.Sleep(1000); }
在 ReceiveAndDelete 模式中,接收作業是單發式作業,也就是,當 Service Bus 接收要求時,它會將訊息標示為正在使用並傳回到應用程式。ReceiveAndDelete 模式是最簡單的模式,最適合用於應用程式容許在發生失敗時不處理訊息的案例。若要了解此種模式,請考慮消費者發出接收要求,而後再處理前當機的案例。因為 Service Bus 將訊息標示為正在使用,所以當應用程式重新啟動並再次開始使用訊息時,將會遺失在當機前使用的訊息。
在 PeekLock 模式中,接收作業變成兩個階段,因而有可能支援不容許遺失訊息的應用程式。當 Service Bus 接收要求時,會發現將要使用的下一個訊息,予以鎖定,以免其他消費者接收該訊息,然後再將它傳回到應用程式。在應用程式處理好訊息 (或可靠地儲存以供未來處理) 之後,於接收的訊息上呼叫 Complete,便完成接收處理程序的第二個階段。當 Service Bus 看見 Complete 時,它會將訊息標示為正在使用。
如果應用程式因為某個原因而無法處理訊息,則可於接收的訊息上呼叫 Abandon 方法 (而非 Complete)。這會導致 Service Bus 將訊息解除鎖定,並使其可供相同的消費者或其他競爭消費者再度接收。其次,鎖定有關聯的逾時設定,如果應用程式無法在鎖定逾時到期前處理訊息 (例如,應用程式當機),則 Service Bus 會將訊息解除鎖定,並使其可供再度接收。
請注意,當應用程式在處理訊息之後,但在 Complete 要求發出之前發生當機,則系統會在應用程式重新啟動時重新傳遞訊息。這通常叫做「至少一次」(At Least Once) 處理;也就是,每則訊息會至少處理一次,但在某些情況下,會重新傳遞相同的訊息。如果此案例無法容許重複處理,則應用程式需要有附加邏輯才能偵測重複項目,此作業可根據訊息的 MessageId 屬性 (在所有傳遞嘗試中維持不變) 來達成。這稱為「正好一次」(Exactly Once) 處理。
如需如何建立訊息以及從佇列往返傳送訊息的詳細資訊和有效範例,請參閱Service Bus 代理訊息 .NET 教學課程。
主題和訂閱
佇列中的每則訊息都是由單一消費者所使用,但主題和訂閱則以「發行/訂閱」模式提供一對多的通訊形式。很適合用於處理非常大量的收件者,向主題登錄的每個訂閱皆可使用每一則發行的訊息。根據可為每一項訂閱設定的篩選規則而定,系統會將訊息傳送到主題並傳遞到一或多個關聯的訂閱。訂閱可使用其他篩選條件來限制所要接收的訊息。訊息傳送到主題與佇列的方式相同,但是訊息不會直接從主題接收,反而會從訂閱接收。主題訂閱類似於虛擬佇列,後者可接收傳送到主題的訊息複本。從訂閱接收訊息與從佇列接收訊息的方式一樣。
透過比較,佇列的訊息傳送功能會直接對應至主題,而其訊息接收功能則會直接對應至訂閱。此外,這還表示訂閱支援這一節中稍早關於佇列所描述的相同模式:競爭消費者、時間解除結合、負載調節和負載平衡。
建立主題是類似於建立佇列的處理程序,如前一節中的範例所示。建立服務 URI,然後使用 NamespaceManager 類別來建立命名空間用戶端。您即可使用 CreateTopic 方法建立主題。例如:
TopicDescription dataCollectionTopic = namespaceClient.CreateTopic("DataCollectionTopic");
接下來,新增您所要的訂閱:
SubscriptionDescription myAgentSubscription = namespaceClient.CreateSubscription(myTopic.Path, "Inventory"); SubscriptionDescription myAuditSubscription = namespaceClient.CreateSubscription(myTopic.Path, "Dashboard");
您可接著建立主題用戶端。例如:
MessagingFactory factory = MessagingFactory.Create(serviceUri, tokenProvider); TopicClient myTopicClient = factory.CreateTopicClient(myTopic.Path)
使用訊息傳送者,您即可從主題往返傳送和接收訊息,如前一節所示。例如:
foreach (BrokeredMessage message in messageList) { myTopicClient.Send(message); Console.WriteLine( string.Format("Message sent: Id = {0}, Body = {1}", message.MessageId, message.GetBody<string>())); }
與佇列類似,訊息是使用 SubscriptionClient 物件而非 QueueClient 物件從訂閱接收。建立訂閱用戶端,並將主題的名稱、訂閱的名稱,以及 (選擇性) 接收模式當作參數傳遞。例如,在 Inventory 訂閱中:
// Create the subscription client MessagingFactory factory = MessagingFactory.Create(serviceUri, tokenProvider); SubscriptionClient agentSubscriptionClient = factory.CreateSubscriptionClient("IssueTrackingTopic", "Inventory", ReceiveMode.PeekLock); SubscriptionClient auditSubscriptionClient = factory.CreateSubscriptionClient("IssueTrackingTopic", "Dashboard", ReceiveMode.ReceiveAndDelete); while ((message = agentSubscriptionClient.Receive(TimeSpan.FromSeconds(5))) != null) { Console.WriteLine("\nReceiving message from Inventory..."); Console.WriteLine(string.Format("Message received: Id = {0}, Body = {1}", message.MessageId, message.GetBody<string>())); message.Complete(); } // Create a receiver using ReceiveAndDelete mode while ((message = auditSubscriptionClient.Receive(TimeSpan.FromSeconds(5))) != null) { Console.WriteLine("\nReceiving message from Dashboard..."); Console.WriteLine(string.Format("Message received: Id = {0}, Body = {1}", message.MessageId, message.GetBody<string>())); }
重要事項 |
|---|
| 如 作法:將服務發行至 Service Bus 登錄 主題中所註明,您可以使用 ServiceRegistrySettings 以指出您是否要讓服務在 Service Bus 上為可搜尋的。如果您的服務是私人服務,則只有知道特定 URI 的個人可以連線。如果是公用服務,則任何人都可以瀏覽 Service Bus 階層並尋找您的接聽程式。不過,無法透過服務登錄公開佇列、主題和訂閱。 |
規則和動作
在許多案例中,必須以特定方法處理具有特定特性的訊息。若要啟用此模式,您可設定訂閱以尋找具有所需屬性的訊息,然後對這些屬性執行特定修改。雖然 Service Bus 訂閱可看見所有傳送至主題的訊息,但您只能將部分的訊息複製到虛擬訂閱佇列。使用訂閱篩選即可完成此作業。這類修改可稱為「篩選動作」。建立訂閱後,您可以提供可在訊息屬性上運作的篩選運算式,而訊息屬性包含系統屬性 (例如,Label) 和應用程式屬性 (例如前一個範例中的 StoreName)。此例中的 SQL 篩選運算式是選用的;若沒有 SQL 篩選運算式,則任何在訂閱上定義的篩選動作都會在該訂閱的所有訊息上執行。
使用前一個範例,若只要篩選來自 Store1 的訊息,您可以建立 Dashboard 訂閱,如下所示:
namespaceManager.CreateSubscription("IssueTrackingTopic", "Dashboard", new SqlFilter("StoreName = 'Store1'"));
若採用此訂閱篩選,則只有 StoreName 屬性設定為 Store1 的訊息會複製到 Dashboard 訂閱的虛擬佇列。
如需可能篩選值的詳細資訊,請參閱 SqlFilter 和 SqlRuleAction 類別的文件。此外,請參閱 Windows Azure SDK中的 AdvancedFiltersSample。
另請參閱
重要事項