匯出 (0) 列印
全部展開

在 Azure 上讓佇列架構傳訊解決方案的延展性和成本效益最大化的最佳作法

更新日期: 2014年7月

作者:Amit Srivastava

審稿者:Brad Calder、Sidney Higa、Christian Martinez、Steve Marx、Curt Peterson、Paolo Salvatori 和 Trace Young

本文提供在 Azure 平台上建立可擴充、高效率且具成本效益的佇列架構傳訊解決方案的規範性指導和最佳作法。本文的預期讀者包括解決方案架構設計人員以及設計和實作雲端架構解決方案的開發人員,這些解決方案會利用 Azure 平台的佇列儲存體服務

傳統的佇列架構傳訊解決方案會利用稱為訊息佇列的訊息儲存位置概念,這是將傳送給一個或多個參與者或是從一個或多個參與者接收而來之資料的儲存機制 (通常是透過非同步通訊機制來傳送)。

本文的目的是要檢驗開發人員如何利用特定的設計模式並搭配 Azure 平台所提供的功能,以建立最佳化而且具成本效益的佇列架構傳訊解決方案。本文會深入探討最常用來在 Azure 解決方案中實作佇列架構互動的方法,並提供改善效能、提高延展性和減少營運費用的建議。

基本討論內容會在適當時混合相關的最佳作法、提示和建議。本文所述的案例會強調以真實世界的客戶專案為根據的技術實作方式。

使用訊息佇列在其分散式元件之間交換資料的典型傳訊解決方案包括「發行者」(將訊息存到佇列中) 以及一個或多個「訂閱者」(預期接收這些訊息)。在大多數情況下,有時稱為「佇列接聽程式」的訂閱者會實作為單一或多執行緒處理序,該處理序會持續執行或是視需要根據排程模式起始。

在較高層級上,有兩個主要分派機制用來讓佇列接聽程式接收儲存在佇列中的訊息:

  • 輪詢 (以提取為基礎的模型):接聽程式定期檢查佇列中是否有新的訊息來監視佇列。當佇列是空的時候,接聽程式會繼續輪詢佇列,藉由進入休眠狀態來定期撤退。

  • 觸發 (以發送為基礎的模型):每當訊息抵達佇列時,接聽程式都會訂閱所觸發的事件 (由發行者本身所觸發或是佇列服務管理員所觸發)。接著接聽程式可能會起始訊息處理,因此不必輪詢佇列來判斷是否有任何新的工作可用。

值得一提的是,這兩個機制有不同的類別。例如,輪詢可以封鎖和解除封鎖。封鎖會讓要求擱置到佇列中出現新的訊息為止 (或是發生逾時),而解除封鎖要求會在佇列上沒有任何東西時立刻完成。當使用觸發模型時,在每次出現新的訊息 (只有當第一個訊息抵達空的佇列時) 或佇列深度到達某個等級時,通知都可以發送給佇列接聽程式。

note附註
Azure 佇列服務 API 支援的清除佇列作業為解除封鎖。這表示,如果佇列中未找到任何訊息,類似 GetMessageGetMessages 的 API 方法將會立即傳回。相反地,Azure Service Bus 佇列則提供封鎖接收作業,該作業會封鎖呼叫的執行緒,直到訊息抵達佇列或是經過指定的逾時期限後。

目前在 Azure 解決方案中最常用來實作佇列接聽程式的方法摘要如下:

  1. 接聽程式會實作為應用程式元件,該元件會當做背景工作角色執行個體的一部分來具現化和執行。

  2. 佇列接聽程式元件的生命週期通常會繫結至主控角色執行個體的執行階段。

  3. 主要的處理邏輯是由一個迴圈所組成,此迴圈中會清除訊息的佇列,並分派訊息以供處理。

  4. 萬一未收到任何訊息,接聽的執行緒會進入休眠狀態,這段期間通常是由應用程式特有的撤退演算法所驅動。

  5. 在接聽程式被通知要結束迴圈並且終止之前,接收迴圈都會處於執行中狀態而且會持續輪詢佇列。

下列流程圖描繪在 Azure 應用程式中使用輪詢機制實作佇列接聽程式時的常用邏輯:

Best-Practices-Messaging-Solutions-Azure2
note附註
為了本文的使用目的,我們不會使用更複雜的設計模式,例如,需要使用中央佇列管理員 (Broker) 的設計模式。

在使用 Azure 佇列時,搭配輪詢機制使用傳統佇列接聽程式可能不是最佳選擇,因為 Azure 計價模型會根據對佇列執行的應用程式要求來衡量儲存體交易,不論佇列是不是空的。以下章節的目的是為了討論在 Azure 平台上將佇列架構傳訊解決方案的效能最大化,以及讓成本降到最低的某些技術。

在本節中,我們必須檢驗如何改善相關的設計層面,以達到更高的效能、更好的延展性和成本效益。

或許,認定某個實作模式是否有資格視為「最有效率的解決方案」的最簡單的方法,就是透過符合以下目標的設計:

  • 減少營運支出:移除不會衍生任何可用工作之儲存體交易的重要部分。

  • 去除過多的延遲:在檢查佇列中是否有新的訊息時,由輪詢間隔所施加的延遲。

  • 動態向上延展或向下延展:針對易變的工作量來調整處理能力。

實作模式也應該符合這些目標,而不會引進遠超過相關利益的複雜層級。

在針對 Azure 平台上部署的解決方案評估擁有權的總成本 (TCO) 和投資報酬率 (ROI) 時,儲存體交易的數量是 TCO 方程式的其中一個主要變數。減少對 Azure 佇列的交易數會降低營運成本,因為這樣與在 Azure 上執行解決方案有關。

與 Azure 佇列相關聯的儲存空間成本可計算如下 –

佇列空間:24 位元組 + Len(QueueName) * 2 +  For-Each 中繼資料 (4 位元組 + Len(佇列名稱) * 2 位元組 + Len(值) * 2 位元組)

訊息空間:12 位元組 + Len(訊息)

在佇列架構傳訊解決方案的環境中,可以使用下列方法的組合來減少儲存體交易的數量:

  1. 將訊息放在佇列中時,將相關訊息分組成較大的單一批次、將壓縮影像壓縮並儲存到 blob 儲存體中,並且使用此佇列來保留用來保存實際資料之 blob 的參考。這種方式有助於獲得最佳的交易成本與儲存空間成本。

  2. 從佇列擷取訊息時,在單一儲存體交易中一起批次處理多個訊息。佇列服務 API 中的 GetMessages 方法允許在單一交易中將指定數目的訊息清除佇列 (請參閱底下的注意事項)。

  3. 在檢查佇列上是否有工作項目存在時,請避免過高的輪詢間隔實作撤退延遲,這樣會在佇列依然持續為空的狀態時增加輪詢要求之間的時間。

  4. 減少佇列接聽程式數目:當使用以提取為基礎的模型時,佇列是空的時候,每個角色執行個體只能使用 1 個佇列接聽程式。為了進一步將每個角色執行個體的佇列接聽程式數目減少為零,請使用通知機制,在佇列接收工作項目時具現化佇列接聽程式。

  5. 如果佇列在大多數的時間都維持空的狀態,請自動減少角色執行個體數目並且繼續監視相關系統度量,以判斷應用程式是否應該擴充執行個體數目來處理增加的工作負載以及何時應該擴充。

  6. 執行相關機制來移除有害訊息。有害訊息通常為格式錯誤因此應用程式無法處理的訊息。如果丟在一旁不處理,這些訊息會累積起來並產生重複的交易與處理成本。有一個簡單的實作機制可以移除佇列中比臨界值期間還久的訊息,並將這些訊息寫入封存系統以便日後進行評估。

  7. 減少預期的逾時錯誤。當您傳送要求到此服務時,您可以指定自己的逾時時間,並將其設為小於 SLA 逾時。在此案例中,如果要求逾時,此要求會被分類到預期的逾時,並且計入可計費的交易。

上述的大部分建議都可以解譯為一種相當通用的實作方式,此方式會處理訊息批次,並且封裝許多基本佇列/blob 儲存體和執行緒管理作業。本文的稍後將會檢驗如何進行這項作業。

Important重要事項
當透過 GetMessages 方法擷取訊息時,佇列服務 API 在單一清除佇列作業中支援的批次大小上限為 32。

一般而言,Azure 佇列交易的成本會隨著佇列服務用戶端數目的增加而呈線性遞增,例如當擴充角色執行個體的數目或增加清除佇列執行緒數目時。為了說明未利用上述建議之解決方案設計的潛在成本影響,我們將會提供以具體數字為後盾的範例。

如果解決方案架構設計人員未實作相關的最佳化,上面描述的計費系統架構可能會在部署解決方案並於 Azure 平台上執行之後,產生過多的營運費用。本節將描述可能發生過多費用的原因。

如同案例定義中所述,商業交易資料會定期送達。不過,假設解決方案在標準 8 小時的工作日期間,只有 25% 的時間忙著處理工作負載。當沒有任何交易通過系統時,這樣會產生 6 小時 (8 小時 * 75%) 的「閒置時間」。此外,此解決方案在每天非上班時間的 16 小時期間將不會收到任何資料。

在一共 22 小時的閒置期間內,此解決方案依然會嘗試執行清除佇列工作,因為它無法明確得知新資料何時送達。在這段期間內,每個個別清除佇列執行緒最多會針對輸入佇列執行 79,200 筆交易 (22 小時 * 60 分鐘 * 每分鐘 60 筆交易),假設預設輪詢間隔為 1 秒。

如同之前所述,Azure 平台中的計價模型是根據個別「儲存體交易」。儲存體交易是使用者應用程式為了新增、讀取、更新或刪除儲存體資料所提出的要求。撰寫這份白皮書時,在 10,000 筆交易中,儲存體交易的計費費率為 $0.01 (不考量任何促銷優惠或特殊價格協議)。

Important重要事項
當計算佇列交易數目時,請記得將單一訊息放在佇列中會算成 1 筆交易,而取用訊息則通常是 2 個步驟的程序,其中牽涉到擷取,然後是從佇列移除訊息的要求。因此,成功的清除佇列作業將會吸引兩筆儲存體交易。請注意,即使清除佇列要求並未擷取任何資料,依然算成可計費的交易。

上述案例的單一清除佇列執行緒所產生的儲存體交易會在每月帳單中增加 $2.38 美元 (79,200 / 10,000 * $0.01 * 30 天)。相較之下,200 個清除佇列執行緒 (或是 200 個背景工作角色執行個體中的 1 個清除佇列執行緒) 每個月會增加 $457.20 美元的成本。這是解決方案未執行任何計算時所發生的成本,此時解決方案只負責檢查佇列,看看是否有任何可用的工作項目。上述範例相當抽象,因為沒有人會以這種方式實作服務,這也是執行接下來描述的最佳化工作極為重要的原因。

若要將佇列架構 Azure 傳訊解決方案的效能最佳化,其中一個方法是使用 Azure Service Bus 所提供的發行/訂閱傳訊層,如本節所述。

在這個方法中,開發人員必須將焦點放在建立輪詢和即時發送架構通知的組合,好讓接聽程式訂閱在某些情況下所引發的通知事件 (觸發程序),以指示新的工作負載被放在佇列中。此方法會使用用於分派通知的發行/訂閱傳訊層來增強傳統佇列輪詢迴圈。

在複雜分散式系統中,這種方法需要使用 Message Bus 或「訊息導向中介軟體」,以確保通知可以鬆散偶合的方式可靠地轉送給一個或多個訂閱者。Azure Service Bus 是用來處理 Azure 上執行與內部部署執行的鬆散偶合分散式應用程式服務之間之傳訊需求的自然選擇。它也非常適合 Message Bus 架構,此架構將會啟用與佇列架構通訊有關之處理序之間的通知交換。

從事佇列架構訊息交換的處理序可能會利用以下模式:

Best-Practices-Messaging-Solutions-Azure3

具體而言,因為它與佇列服務發行者和訂閱者之間的互動有關,所以適用於 Azure 角色執行個體之間通訊的原則同樣符合以發送為基礎之通知訊息交換的大部分要求。

Important重要事項
Azure Service Bus 的使用受限於計價模型,計價模型會考量針對 Message Bus 傳訊實體 (例如佇列或主題) 執行的傳訊作業數量。

因此,執行成本效益分析來評估在特定架構中引進 Service Bus 的利弊非常重要。除此之外,也值得評估引進以 Service Bus 為基礎的通知分派層是否會真的導致成本降低,因而證明投資與額外的開發工作是值得的。

如需有關 Service Bus 計價模型的詳細資訊,請參閱 Azure 平台常見問題集的相關章節。

雖然使用發行/訂閱傳訊層來處理對延遲的影響相當輕鬆,您可以使用動態 (彈性) 調整來進一步實現成本的降低,如下一節所述。

Azure 儲存體在整體帳戶層級與每一資料分割層級定義延展性目標。Azure 中的佇列是它自己的單一分割資料,因此每秒可以處理多達 2000 則訊息。當訊息數量超過此配額,儲存體服務會回覆 HTTP 503 伺服器忙碌中的訊息。此訊息表示平台正對此佇列進行節流。應用程式設計者應進行容量規劃,確保有適當的佇列數量可支應應用程式要求率。如果單一佇列無法應付應用程式的要求率,請設計分割的佇列架構,以多個佇列來確保延展性。

應用程式也可以運用數個不同的佇列來處理不同的訊息類型。如此即可藉由允許多個佇列共存以免阻塞單一佇列,來確保應用程式的延展性。此外,也能根據儲存於不同佇列之訊息的敏感性與優先順序,個別控制佇列的處理方式。優先順序較高的佇列可獲得較多的專用背景工作。

Azure 平台讓客戶可以前所未有的快速和輕鬆方式向上和向下延展。能夠根據易變的工作負載和變動的流量進行調整是雲端平台的其中一個主要價值主張。這表示,「延展性」不再是一個耗費成本的 IT 詞彙而已,而是一個現成的功能,使用者可以在架構良好的雲端解決方案中視需要以程式設計方式加以啟用。

「動態調整」是給定解決方案為了配合變動的工作負載而進行調整的技術性功能,其調整方式是在執行階段增加及減少工作能力和處理能力。Azure 平台原本就可透過佈建分散式運算基礎結構來支援動態調整 (可以視需要在此基礎結構上購買運算時數)。

區分 Azure 平台上的以下兩種動態調整類型非常重要:

  • 角色執行個體調整指的是新增和移除其他 Web 或背景工作角色執行個體來處理時間點的工作負載。這通常包含變更服務組態中的執行個體計數。提高執行個體計數將會造成 Azure 執行階段啟動新的執行個體,而減少執行個體計數則會讓它關閉執行中的執行個體。

  • 處理序 (執行緒) 調整指的是根據給定角色執行個體中的處理中執行緒來維護足夠的容量,維護的方式是根據目前的工作負載向上和向下調整執行緒的數目。

佇列架構傳訊解決方案中的動態調整將會吸引以下一般建議的組合:

  1. 監視關鍵效能指標包括 CPU 使用量、佇列深度、回應時間和訊息處理延遲。

  2. 動態增加或減少角色執行個體數目可處理工作負載的尖峰,不論是可預測還是無法預測。

  3. 以程式設計方式擴充及修剪處理中執行緒的數目可配合給定角色執行個體處理之變動負載條件來進行調整。

  4. 同時分割和處理細部工作負載,它會在 .NET Framework 4 中使用工作平行程式庫

  5. 在解決方案中維護有效的容量,並且具有高易變性的工作負載,以便在突然出現尖峰時能夠加以處理,而不用負擔設定其他執行個體的成本。

服務管理 API 可讓 Azure 託管服務能夠藉由在執行階段變更部署組態來修改其執行中角色執行個體的數目。

note附註
在典型的訂閱中,Azure 小型運算執行個體數目上限 (或是以核心數目計算時,則是其他規模的運算執行個體的同等數目) 預設限制為 20。提高這個配額的所有要求都應該由 Azure 支援團隊提出。如需詳細資訊,請參閱 Azure 平台常見問題集

Azure 自動調整推出後,此平台可以依據佇列訊息深度調高或調低執行個體的數量。此功能相當適合動態調整作業。額外好處是 Azure 平台可監視與調整應用程式的工作。

角色執行個體計數的動態調整不見得是處理負載尖峰的最適合的選擇。例如,新的角色執行個體需要花幾秒鐘的時間來向上微調,而且目前沒有提供有關向上微調持續時間的任何 SLA 度量。不過,解決方案可能只需要增加工作者執行緒的數目,以處理易變的工作負載增加。正在處理工作負載時,解決方案將會監視相關的負載度量,並判斷它是否需要動態減少或增加背景工作處理序的數目。

Important重要事項
目前,單一 Azure 佇列的延展性目標「受限於」每秒 2000 筆交易。如果應用程式嘗試超出此目標,例如,透過從執行數百個清除佇列執行緒的多個角色執行個體執行佇列作業,它可能會從儲存體服務產生 HTTP 503「伺服器忙碌」的回應。發生這種狀況時,應用程式應該使用指數型撤退延遲演算法來實作重試機制。不過,如果定期出現 HTTP 503 錯誤,建議您使用多個佇列並且實作資料分割架構策略,橫跨多個佇列進行調整。

在許多情況下,自動調整背景工作處理序是個別角色執行個體的責任。相反地,角色執行個體的調整通常牽涉到解決方案架構的中央元素,此元素負責監視效能度量並採取適當的調整動作。下圖描繪一個稱為「動態調整代理程式」的服務元件,此元件會收集及分析負載度量,以判斷它是否需要佈建新的執行個體或是讓閒置的執行個體退役。

Best-Practices-Messaging-Solutions-Azure4

請注意,此調整代理程式服務可以部署為 Azure 上執行的背景工作角色或是部署為內部部署服務。不論部署拓撲為何,此服務都能夠存取 Azure 佇列。

若要實作動態調整功能,請考慮使用 Microsoft 企業程式庫自動調整應用程式區塊,此區塊會在 Azure 上執行的解決方案中啟用自動調整行為。此自動調整應用程式區塊會提供在 Azure 應用程式中定義和監視自動調整所需的所有功能。

note附註
您也可以不要使用手動動態調整,改為考慮使用 Azure 的內建自動調整功能。

現在我們已涵蓋延遲影響、儲存體交易成本和動態調整需求,現在是將我們的建議合併到技術實作中的好時機。

為了提供具體的範例,我們將會依照以下方式歸納出真實世界的客戶案例。

SaaS 解決方案提供者推出了一個新的計費系統,此系統實作為 Azure 應用程式,能夠服務大規模之客戶交易處理的商業需求。解決方案的關鍵前提在於能夠將運算密集的工作負載卸載到雲端,並充分利用 Azure 基礎結構的彈性來執行運算密集的工作。

端對端架構的內部部署元素會在一整天中,定期將大量交易合併及分派到 Azure 託管服務。每次提交的數量不一,可能從幾千筆到幾十萬筆都有,每天會有高達數百萬筆的交易。此外,假設解決方案必須符合 SLA 針對最大處理延遲保證所推動的需求。

解決方案架構是以分散式 map-reduce 設計模式為基礎,而且包含了多執行個體的背景工作角色架構雲端層 (這一層會使用 Azure 佇列儲存體分派工作)。交易批次會由 Process Initiator 背景工作角色執行個體所接受、分解 (解除批次) 成較小的工作項目,並加入 Azure 佇列集合的佇列中,以便分配負載。

工作負載處理是由處理背景工作角色的多個執行個體所處理,從佇列提取工作項目,並透過運算程序加以傳遞。處理執行個體會採用多執行緒的佇列接聽程式,以實作平行資料處理來獲得最佳效能。

處理過的工作項目會路由傳送到專用佇列,Process Controller 背景工作角色執行個體會從此佇列清除這些項目的佇列、將其彙總並保存到資料存放區,以進行資料採礦、報告和分析。

解決方案架構描繪如下:

AzureGuidance_MaxScale

上圖描繪一個典型的架構,用於向外延展大型或複雜的運算工作負載。此架構所採用的佇列架構訊息交換模式對於許多其他 Azure 應用程式和服務而言也很常見 (這些應用程式和服務需要透過佇列彼此互相通訊)。這樣會採用標準方法來檢查與佇列架構訊息交換有關的特定根本元件。

若要讓 Azure 平台上執行之佇列架構傳訊解決方案的效率和成本效益最大化,解決方案架構設計人員和開發人員應該考慮下列建議。

您身為解決方案架構設計人員,應該進行以下作業:

  • 佈建以佇列為基礎的傳訊架構,此架構會在雲端架構或混合式解決方案的不同層與服務之間使用 Azure 佇列儲存體服務,以進行高等級的非同步通訊

  • 建議使用資料分割佇列架構,以擴充到超過每秒 2000 則訊息。

  • 了解 Azure 計價模型的基本面,並透過一系列的最佳作法和設計模式最佳化解決方案來降低交易成本

  • 藉由佈建一個可配合易變和波動的工作負載進行調整的架構來考慮動態調整需求。

  • 運用適當的自動調整技術和方法,彈性地擴充和縮小電腦能力,以進一步地最佳化營運費用。

  • 評估 Azure 自動調整功能是否適合應用程式的動態調整需求。

  • 藉由相依於 Azure Service Bus 來進行即時的發送架構通知分派,以評估降低延遲的成本效益比。

您身為開發人員,應該進行以下作業:

  • 設計一種傳訊解決方案,當從 Azure 佇列儲存和擷取資料時,此解決方案會運用批次處理

  • 評估 Azure 自動調整功能是否適合應用程式的動態調整需求。

  • 實作高效率的佇列接聽程式服務,以確保當佇列是空的時候,將由一個清除佇列執行緒的最大值輪詢佇列

  • 當佇列在一段很長的時間依然是空的時候,動態往下調整背景工作角色執行個體的數目

  • 實作應用程式特有的隨機指數型撤退演算法,以減少閒置佇列輪詢對於儲存體交易成本的影響。

  • 採用適當的技術,以免在實作高度多執行緒的多個執行個體佇列發行者和取用者時,超出單一佇列的延展性目標

  • 運用強固的重試原則,在發行和取用 Azure 佇列中的資料時,此原則將能夠處理各種不同的短暫狀況。

  • 使用 Azure Service Bus 提供的單向事件功能來支援以發送為基礎的通知,以減少延遲及改善以佇列為基礎的傳訊解決方案效能。

  • 探索 .NET Framework 4 的新功能,例如 TPL、PLINQ 和觀察器模式,以最大化平行處理原則的程度、改善並行存取,並且簡化多執行緒服務的設計。

隨附的範例程式碼可從 MSDN Code Gallery 下載。此範例程式碼也包含所有必要的基礎結構元件,例如 Azure 佇列服務適用的可感知泛型的抽象層,上述程式碼片段中並未提供這些元件。請注意,所有原始程式碼檔案都受到 Microsoft 公用授權所控管,如同對應的法律聲明所述。

如需本白皮書中所討論之主題的詳細資訊,請參閱下列文件:

顯示:
© 2014 Microsoft