(十六)——優(yōu)先級(jí)隊(duì)列模式

2018-02-24 15:44 更新

云計(jì)算設(shè)計(jì)模式(十六)——優(yōu)先級(jí)隊(duì)列模式

優(yōu)先發(fā)送到服務(wù),以便具有較高優(yōu)先級(jí)的請(qǐng)求被接收和高于一個(gè)較低優(yōu)先級(jí)的更快速地處理請(qǐng)求。這種模式是在應(yīng)用程序是有用的,它提供不同的服務(wù)級(jí)別保證或者針對(duì)獨(dú)立客戶。

背景和問題

應(yīng)用程序可以委托給其他服務(wù)的具體任務(wù);例如,為了執(zhí)行后臺(tái)處理或與其他應(yīng)用程序或服務(wù)的整合。在云中,消息隊(duì)列通常用于將任務(wù)委派給后臺(tái)處理。在許多情況下,請(qǐng)求由服務(wù)接收的順序是不重要的。然而,在某些情況下,可能需要優(yōu)先考慮的具體要求。這些要求必須早于較低優(yōu)先級(jí)的其他可能先前已發(fā)送由應(yīng)用程序進(jìn)行處理。

解決方案

隊(duì)列通常是先入先出(FIFO)結(jié)構(gòu),而消費(fèi)者通常會(huì)收到他們發(fā)布到隊(duì)列中的順序相同的消息。然而,一些消息隊(duì)列支持優(yōu)先級(jí)的消息傳遞;應(yīng)用程序發(fā)布一條消息可以分配優(yōu)先級(jí)的消息,并在隊(duì)列中的消息會(huì)自動(dòng)重新排序,使得具有較高優(yōu)先級(jí)的消息將這些優(yōu)先級(jí)較低的前被接收。圖1示出了一個(gè)隊(duì)列,它提供優(yōu)先權(quán)的消息。

圖1 - 使用支持消息優(yōu)先級(jí)排隊(duì)機(jī)制

注意: 大多數(shù)消息隊(duì)列的實(shí)現(xiàn)支持多個(gè)消費(fèi)者(以下的競(jìng)爭(zhēng)消費(fèi)者模式)和消費(fèi)過程的數(shù)量可以按比例增加或減小的需求支配。

在不支持基于優(yōu)先級(jí)的消息隊(duì)列系統(tǒng)中,一種替代的解決方案是為每一個(gè)優(yōu)先級(jí)的獨(dú)立隊(duì)列。該應(yīng)用程序負(fù)責(zé)將郵件投遞到適當(dāng)?shù)年?duì)列。每個(gè)隊(duì)列可以有一個(gè)單獨(dú)的消費(fèi)者池。高優(yōu)先級(jí)隊(duì)列可以有更快的硬件比低優(yōu)先級(jí)隊(duì)列中運(yùn)行的消費(fèi)者一個(gè)更大的泳池。圖2示出了這種方法。

圖2 - 使用不同的消息隊(duì)列為每個(gè)優(yōu)先級(jí)

這種策略的變化是有消費(fèi)者認(rèn)為檢查對(duì)高優(yōu)先級(jí)隊(duì)列中的消息,然后再才開始從低優(yōu)先級(jí)隊(duì)列中讀取消息,如果沒有更高優(yōu)先級(jí)的消息都在等待的一個(gè)池。還有,使用消費(fèi)過程的一個(gè)池的溶液之間的一些語義差異(或者使用支持不同的優(yōu)先級(jí)或多個(gè)隊(duì)列,每個(gè)處理一個(gè)單一的優(yōu)先級(jí)消息的消息的單個(gè)隊(duì)列),以及使用多個(gè)隊(duì)列用溶液為每個(gè)隊(duì)列一個(gè)單獨(dú)的游泳池。

在單池的做法,高優(yōu)先級(jí)的消息總是會(huì)收到以前低優(yōu)先級(jí)的消息處理。在理論中,具有非常低的優(yōu)先級(jí)的消息可以被不斷地取代,并且可能永遠(yuǎn)不會(huì)被處理。在多池的方法,較低優(yōu)先級(jí)的報(bào)文將總是被處理,只是不一樣迅速的那些更高的優(yōu)先級(jí)的(取決于池和它們具有可用資源的相對(duì)大?。?/p>

使用優(yōu)先級(jí)排隊(duì)機(jī)制可提供以下優(yōu)點(diǎn):

  • 它允許應(yīng)用程序以滿足必要的可用性或性能優(yōu)先的業(yè)務(wù)需求,如提供不同級(jí)別的服務(wù),以客戶的特定群體。
  • 它可以幫助最大限度地降低運(yùn)營(yíng)成本。在單隊(duì)列的方式,你可以縮減用戶的數(shù)量,如果有必要的。高優(yōu)先級(jí)消息仍將被首先處理(雖然可能更慢),和低優(yōu)先級(jí)的消息可能會(huì)延遲更長(zhǎng)。如果您已實(shí)現(xiàn)與消費(fèi)者的每一個(gè)單獨(dú)的隊(duì)列池多個(gè)消息隊(duì)列的方式,可以減少消費(fèi)者的池低優(yōu)先級(jí)隊(duì)列,或者甚至停止所有監(jiān)聽的訊息的消費(fèi)者暫停處理一些非常低優(yōu)先級(jí)隊(duì)列這些隊(duì)列。
  • 在多個(gè)消息隊(duì)列的方法可以幫助劃分的基礎(chǔ)上處理要求的消息,以最大限度地提高應(yīng)用程序的性能和可擴(kuò)展性。例如,重要的任務(wù),可以優(yōu)先被立即運(yùn)行,而不太重要的后臺(tái)任務(wù)可以被安排在不太繁忙的時(shí)段運(yùn)行的接收器來處理接收處理。

問題和注意事項(xiàng)

在決定如何實(shí)現(xiàn)這個(gè)模式時(shí),請(qǐng)考慮以下幾點(diǎn):

  • 定義優(yōu)先級(jí)的解決方案的情況下。例如,“高優(yōu)先級(jí)”可能意味著,信息應(yīng)該在十秒內(nèi)進(jìn)行處理。標(biāo)識(shí)要求處理高優(yōu)先級(jí)的項(xiàng)目,以及其他什么資源必須分配給符合這些標(biāo)準(zhǔn)。
  • 確定是否所有高優(yōu)先級(jí)的項(xiàng)目必須在任何優(yōu)先級(jí)較低的項(xiàng)目之前進(jìn)行處理。如果該消息是由消費(fèi)者的一個(gè)池被處理,可能有必要提供一種可搶先和暫停正在處理的低優(yōu)先級(jí)消息,如果更高優(yōu)先級(jí)的消息,有一個(gè)任務(wù)的機(jī)制。
  • 在多個(gè)隊(duì)列中的方法,使用該監(jiān)聽所有的隊(duì)列,而不是一個(gè)專門的客戶池的每個(gè)隊(duì)列的消費(fèi)過程的一個(gè)池時(shí),消費(fèi)者必須應(yīng)用一種算法,以確保它總是從那些從低之前較高優(yōu)先級(jí)的隊(duì)列提供服務(wù)的消息優(yōu)先級(jí)隊(duì)列。
  • 監(jiān)視處理的高和低優(yōu)先級(jí)隊(duì)列中的速度,以確保在這些隊(duì)列中的消息的預(yù)期的速率進(jìn)行處理。
  • 如果需要,以保證低優(yōu)先級(jí)的消息將被處理時(shí),可能有必要實(shí)現(xiàn)與消費(fèi)者的多個(gè)池的多個(gè)消息隊(duì)列的方法?;蛘?,在一個(gè)支持消息優(yōu)先隊(duì)列,它可能會(huì)動(dòng)態(tài)地增加一個(gè)排隊(duì)的消息的優(yōu)先級(jí),因?yàn)樗哪挲g。然而,該方法依賴于消息隊(duì)列提供此功能。
  • 使用單獨(dú)的隊(duì)列中每個(gè)消息優(yōu)先級(jí)最適合有少數(shù)明確定義的優(yōu)先級(jí)系統(tǒng)。
  • 消息優(yōu)先級(jí)可以通過系統(tǒng)邏輯決定的。例如,而不是明確的高和低優(yōu)先級(jí)的消息,他們可以被指定為“自費(fèi)客戶”,或“非自費(fèi)的客戶?!备鶕?jù)您的商業(yè)模式,你的系統(tǒng)可能會(huì)分配更多的資源,從收費(fèi)處理消息付費(fèi)用戶比非自費(fèi)的。
  • 有可能是檢查隊(duì)列的消息相關(guān)聯(lián)的金融和處理成本(一些商業(yè)郵件系統(tǒng)的消息被發(fā)布或檢索每次收取一小筆費(fèi)用,每次一個(gè)隊(duì)列中查詢消息)。檢查多個(gè)隊(duì)列時(shí),該成本將有所增加。
  • 它可以是能夠動(dòng)態(tài)調(diào)整的基礎(chǔ)上,該池所服務(wù)的隊(duì)列的長(zhǎng)度消費(fèi)者的一個(gè)池的大小。欲了解更多信息,請(qǐng)參閱自動(dòng)縮放指導(dǎo)。

何時(shí)使用這個(gè)模式

這種模式非常適合場(chǎng)景:

  • 系統(tǒng)必須處理可能有不同的側(cè)重點(diǎn)多個(gè)任務(wù)。
  • 不同的用戶或租戶應(yīng)配以不同的優(yōu)先級(jí)。

例子

微軟 Azure 不提供經(jīng)過整理的本地支持郵件自動(dòng)優(yōu)先級(jí)排隊(duì)機(jī)制。然而,它確實(shí)提供了 Azure 的服務(wù)總線主題和訂閱,支持排隊(duì)機(jī)制,提供郵件過濾,具有多種靈活的功能,使其非常適合用在幾乎所有的優(yōu)先級(jí)隊(duì)列的實(shí)現(xiàn)在一起。

一個(gè) Azure 的解決方案,可以實(shí)現(xiàn)服務(wù)總線話題,其中一個(gè)應(yīng)用程序可以發(fā)布消息,以同樣的方式作為一個(gè)隊(duì)列。消息可以包含在應(yīng)用程序定義的自定義屬性的形式的元數(shù)據(jù)。服務(wù)總線訂閱可以與主題相關(guān)聯(lián),并且這些訂閱可以篩選根據(jù)它們的屬性信息。當(dāng)一個(gè)應(yīng)用程序?qū)⑾l(fā)送到一個(gè)主題,該消息被定向到從那里它可以被消費(fèi)者閱讀相應(yīng)的訂閱。消費(fèi)者的過程可以檢索使用相同的語義消息隊(duì)列(訂閱是一個(gè)邏輯隊(duì)列)從一個(gè)訂閱消息。

圖 3 示出了使用的 Azure 服務(wù)總線主題和訂閱的解決方案

圖3 - 實(shí)現(xiàn)與 Azure 的服務(wù)總線主題和訂閱優(yōu)先級(jí)隊(duì)列

在圖3中的應(yīng)用程序創(chuàng)建多個(gè)消息和每個(gè)消息與價(jià)值分配被稱為優(yōu)先級(jí)的自定義屬性,無論是高還是低。該應(yīng)用程序的帖子,這些消息的一個(gè)話題。這個(gè)主題有兩個(gè)相關(guān)的訂閱,這兩個(gè)濾波器的消息通過檢查優(yōu)先級(jí)屬性。一位接受認(rèn)購(gòu),其中優(yōu)先級(jí)屬性設(shè)置為高的消息,而其他接受其中優(yōu)先級(jí)屬性設(shè)置為低的消息。消費(fèi)者池讀取每個(gè)訂閱的消息。高優(yōu)先認(rèn)購(gòu)有較大的游泳池,而這些消費(fèi)者可能會(huì)更強(qiáng)大(且昂貴)的計(jì)算機(jī)上運(yùn)行有提供比消費(fèi)者在低優(yōu)先級(jí)池的更多資源。

請(qǐng)注意,沒有什么特別的高,低優(yōu)先級(jí)消息在這個(gè)例子中指定。這些僅僅是指定為每個(gè)消息中的屬性的標(biāo)簽,并用于引導(dǎo)消息發(fā)送到一個(gè)特定的訂閱。如果附加的優(yōu)先級(jí)是必需的,它是比較容易地創(chuàng)建進(jìn)一步的訂閱和消費(fèi)者進(jìn)程池來處理這些優(yōu)先級(jí)。

在可用于此引導(dǎo)代碼時(shí) Queue 解決方案包含這種方法的一個(gè)實(shí)現(xiàn)。該解決方案包含一個(gè)名為 PriorityQueue.High 和 PriorityQueue.Low 兩個(gè)工作角色的項(xiàng)目。這兩個(gè)輔助角色繼承的類被稱為 PriorityWorkerRole 它包含用于連接到一個(gè)指定的預(yù)訂中 OnStart 方法的功能。

該 PriorityQueue.High 和 PriorityQueue.Low 輔助角色連接到不同的預(yù)訂,他們的配置設(shè)置來定義。管理員可以配置每個(gè)角色的不同數(shù)量要運(yùn)行;通常會(huì)有比 PriorityQueue.Low 工作者角色的 PriorityQueue.High 輔助角色更多的實(shí)例。

在 PriorityWorkerRole 類的 Run 方法安排虛擬 ProcessMessage 的方法(在 PriorityWorkerRole 類定義)的隊(duì)列中接收到的每個(gè)消息被執(zhí)行。下面的代碼顯示了運(yùn)行和 ProcessMessage 的方法。在類的 QueueManager,在 PriorityQueue.Shared 項(xiàng)目定義,提供了輔助方法使用的 Azure 服務(wù)總線隊(duì)列。

public class PriorityWorkerRole : RoleEntryPoint  
{  
  private QueueManager queueManager;  
  ...  
?
  public override void Run()  
  {  
    // Start listening for messages on the subscription.  
    var subscriptionName = CloudConfigurationManager.GetSetting("SubscriptionName");  
    this.queueManager.ReceiveMessages(subscriptionName, this.ProcessMessage);  
    ...;  
  }  
  ...  
?
  protected virtual async Task ProcessMessage(BrokeredMessage message)  
  {  
    // Simulating processing.  
    await Task.Delay(TimeSpan.FromSeconds(2));  
  }  
}

該 PriorityQueue.High 和 PriorityQueue.Low 輔助角色既覆蓋 ProcessMessage 的方法的默認(rèn)功能。下面的代碼顯示了 ProcessMessage 的方法為 PriorityQueue.High 輔助角色。

Copy  
?
?
protected override async Task ProcessMessage(BrokeredMessage message)  
{  
  // Simulate message processing for High priority messages.  
  await base.ProcessMessage(message);  
  Trace.TraceInformation("High priority message processed by " +  
    RoleEnvironment.CurrentRoleInstance.Id + " MessageId: " + message.MessageId);  
}

當(dāng)一個(gè)應(yīng)用程序?qū)⑾l(fā)布到與所使用的 PriorityQueue.High 和 PriorityQueue.Low 輔助角色的訂閱相關(guān)聯(lián)的主題,它指定了優(yōu)先使用優(yōu)先級(jí)的自定義屬性,如在下面的代碼示例。此代碼(這是在 PriorityQueue.Sender項(xiàng)目 WorkerRole 類實(shí)現(xiàn)),使用的 QueueManager 類的 SendBatchAsync 輔助方法發(fā)帖分批的話題。

// Send a low priority batch.   
var lowMessages = new List<BrokeredMessage>();  
?
for (int i = 0; i < 10; i++)  
{  
  var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };  
  message.Properties["Priority"] = Priority.Low;  
  lowMessages.Add(message);  
}  
?
this.queueManager.SendBatchAsync(lowMessages).Wait();  
...  
?
// Send a high priority batch.  
var highMessages = new List<BrokeredMessage>();  
?
for (int i = 0; i < 10; i++)  
{  
  var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };  
  message.Properties["Priority"] = Priority.High;  
  highMessages.Add(message);  
}  
?
this.queueManager.SendBatchAsync(highMessages).Wait();
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)