在 Windows Azure 上讓佇列架構傳訊解決方案的延展性和成本效益最大化的最佳作法
作者:Valery Mizonov
審稿者:Brad Calder、Sidney Higa、Christian Martinez、Steve Marx、Curt Peterson、Paolo Salvatori 和 Trace Young
本文提供在 Windows Azure 平台上建立可擴充、高效率且具成本效益的佇列架構傳訊解決方案的規範性指導和最佳作法。本文的預期讀者包括解決方案架構設計人員以及設計和實作雲端架構解決方案的開發人員,這些解決方案會利用 Windows Azure 平台的佇列儲存體服務。
摘要
傳統的佇列架構傳訊解決方案會利用稱為訊息佇列的訊息儲存位置概念,這是將傳送給一個或多個參與者或是從一個或多個參與者接收而來之資料的儲存機制 (通常是透過非同步通訊機制來傳送)。
佇列架構的資料交換代表了可靠而且可高度擴充的訊息架構基礎,此架構能夠在分散式運算環境中支援各種不同的強大案例。不論是大量的工作分派還是持久的傳訊,訊息佇列技術都可以介入並提供一流的功能,以滿足大規模之非同步通訊的不同需求。
本文的目的是要檢驗開發人員如何利用特定的設計模式並搭配 Windows Azure 平台所提供的功能,以建立最佳化而且具成本效益的佇列架構傳訊解決方案。本文會深入探討最常用來在 Windows Azure 解決方案中實作佇列架構互動的方法,並提供改善效能、提高延展性和減少營運費用的建議。
基本討論內容會在適當時混合相關的最佳作法、提示和建議。本文所述的案例會強調以真實世界的客戶專案為根據的技術實作方式。
客戶案例
為了提供具體的範例,我們將會依照以下方式歸納出真實世界的客戶案例。
SaaS 解決方案提供者推出了一個新的計費系統,此系統實作為 Windows Azure 應用程式,能夠服務大規模之客戶交易處理的商業需求。解決方案的關鍵前提在於能夠將運算密集的工作負載卸載到雲端,並充分利用 Windows Azure 基礎結構的彈性來執行運算密集的工作。
端對端架構的內部部署元素會在一整天中,定期將大量交易合併及分派到 Windows Azure 託管服務。每次提交的數量不一,可能從幾千筆到幾十萬筆都有,每天會有高達數百萬筆的交易。此外,假設解決方案必須符合 SLA 針對最大處理延遲保證所推動的需求。
解決方案架構是以分散式 map-reduce 設計模式為基礎,而且包含了多執行個體的背景工作角色架構雲端層 (這一層會使用 Windows Azure 佇列儲存體分派工作)。交易批次會由 Process Initiator 背景工作角色執行個體所接受、分解 (解除批次) 成較小的工作項目,並加入 Windows Azure 佇列集合的佇列中,以便分配負載。
工作負載處理是由處理背景工作角色的多個執行個體所處理,從佇列提取工作項目,並透過運算程序加以傳遞。處理執行個體會採用多執行緒的佇列接聽程式,以實作平行資料處理來獲得最佳效能。
處理過的工作項目會路由傳送到專用佇列,Process Controller 背景工作角色執行個體會從此佇列清除這些項目的佇列、將其彙總並保存到資料存放區,以進行資料採礦、報告和分析。
解決方案架構描繪如下:
上圖描繪一個典型的架構,用於向外延展大型或複雜的運算工作負載。此架構所採用的佇列架構訊息交換模式對於許多其他 Windows Azure 應用程式和服務而言也很常見 (這些應用程式和服務需要透過佇列彼此互相通訊)。這樣會採用標準方法來檢查與佇列架構訊息交換有關的特定根本元件。
佇列架構傳訊基本面
使用訊息佇列在其分散式元件之間交換資料的典型傳訊解決方案包括「發行者」(將訊息存到佇列中) 以及一個或多個「訂閱者」(預期接收這些訊息)。在大多數情況下,有時稱為「佇列接聽程式」的訂閱者會實作為單一或多執行緒處理序,該處理序會持續執行或是視需要根據排程模式起始。
在較高層級上,有兩個主要分派機制用來讓佇列接聽程式接收儲存在佇列中的訊息:
-
輪詢 (以提取為基礎的模型):接聽程式定期檢查佇列中是否有新的訊息來監視佇列。當佇列是空的時候,接聽程式會繼續輪詢佇列,藉由進入休眠狀態來定期撤退。
-
觸發 (以發送為基礎的模型):每當訊息抵達佇列時,接聽程式都會訂閱所觸發的事件 (由發行者本身所觸發或是佇列服務管理員所觸發)。接著接聽程式可能會起始訊息處理,因此不必輪詢佇列來判斷是否有任何新的工作可用。
值得一提的是,這兩個機制有不同的類別。例如,輪詢可以封鎖和解除封鎖。封鎖會讓要求擱置到佇列中出現新的訊息為止 (或是發生逾時),而解除封鎖要求會在佇列上沒有任何東西時立刻完成。當使用觸發模型時,在每次出現新的訊息 (只有當第一個訊息抵達空的佇列時) 或佇列深度到達某個等級時,通知都可以發送給佇列接聽程式。
附註 |
|---|
| Windows Azure 佇列服務 API 支援的清除佇列作業為解除封鎖。這表示,如果佇列中未找到任何訊息,類似 GetMessage 或 GetMessages 的 API 方法將會立即傳回。相反地,Windows Azure Service Bus 佇列則提供封鎖接收作業,該作業會封鎖呼叫的執行緒,直到訊息抵達佇列或是經過指定的逾時期限後。 |
目前在 Windows Azure 解決方案中最常用來實作佇列接聽程式的方法摘要如下:
-
接聽程式會實作為應用程式元件,該元件會當做背景工作角色執行個體的一部分來具現化和執行。
-
佇列接聽程式元件的生命週期通常會繫結至主控角色執行個體的執行階段。
-
主要的處理邏輯是由一個迴圈所組成,此迴圈中會清除訊息的佇列,並分派訊息以供處理。
-
萬一未收到任何訊息,接聽的執行緒會進入休眠狀態,這段期間通常是由應用程式特有的撤退演算法所驅動。
-
在接聽程式被通知要結束迴圈並且終止之前,接收迴圈都會處於執行中狀態而且會持續輪詢佇列。
下列流程圖描繪在 Windows Azure 應用程式中使用輪詢機制實作佇列接聽程式時的常用邏輯:
附註 |
|---|
| 為了本文的使用目的,我們不會使用更複雜的設計模式,例如,需要使用中央佇列管理員 (Broker) 的設計模式。 |
在使用 Windows Azure 佇列時,搭配輪詢機制使用傳統佇列接聽程式可能不是最佳選擇,因為 Windows Azure 計價模型會根據對佇列執行的應用程式要求來衡量儲存體交易,不論佇列是不是空的。以下章節的目的是為了討論在 Windows Azure 平台上將佇列架構傳訊解決方案的效能最大化,以及讓成本降到最低的某些技術。
效能、延展性與成本最佳化的最佳作法
在本節中,我們必須檢驗如何改善相關的設計層面,以達到更高的效能、更好的延展性和成本效益。
或許,認定某個實作模式是否有資格視為「最有效率的解決方案」的最簡單的方法,就是透過符合以下目標的設計:
-
減少營運支出:移除不會衍生任何可用工作之儲存體交易的重要部分。
-
去除過多的延遲:在檢查佇列中是否有新的訊息時,由輪詢間隔所施加的延遲。
-
動態向上延展或向下延展:針對易變的工作量來調整處理能力。
實作模式也應該符合這些目標,而不會引進遠超過相關利益的複雜層級。
最佳化儲存體交易成本的最佳作法
在針對 Windows Azure 平台上部署的解決方案評估擁有權的總成本 (TCO) 和投資報酬率 (ROI) 時,儲存體交易的數量是 TCO 方程式的其中一個主要變數。減少對 Windows Azure 佇列的交易數會降低營運成本,因為這樣與在 Windows Azure 上執行解決方案有關。
在佇列架構傳訊解決方案的環境中,可以使用下列方法的組合來減少儲存體交易的數量:
-
將訊息放在佇列中時,將相關訊息分組成較大的單一批次、將壓縮影像壓縮並儲存到 blob 儲存體中,並且使用此佇列來保留用來保存實際資料之 blob 的參考。
-
從佇列擷取訊息時,在單一儲存體交易中一起批次處理多個訊息。佇列服務 API 中的 GetMessages 方法會啟用單一交易中指定之訊息數目的清除佇列作業 (請參閱底下的注意事項)。
-
在檢查佇列上是否有工作項目存在時,請避免過高的輪詢間隔及實作撤退延遲,這樣會在佇列依然持續為空的狀態時增加輪詢要求之間的時間。
-
減少佇列接聽程式數目:當使用以提取為基礎的模型時,佇列是空的時候,每個角色執行個體只能使用 1 個佇列接聽程式。為了進一步將每個角色執行個體的佇列接聽程式數目減少為零,請使用通知機制,在佇列接收工作項目時具現化佇列接聽程式。
-
如果佇列在大多數的時間都維持空的狀態,請自動減少角色執行個體數目並且繼續監視相關系統度量,以判斷應用程式是否應該擴充執行個體數目來處理增加的工作負載以及何時應該擴充。
上述的大部分建議都可以解譯為一種相當通用的實作方式,此方式會處理訊息批次,並且封裝許多基本佇列/blob 儲存體和執行緒管理作業。本文的稍後將會檢驗如何進行這項作業。
重要事項 |
|---|
| 當透過 GetMessages 方法擷取訊息時,佇列服務 API 在單一清除佇列作業中支援的批次大小上限為 32。 |
一般而言,Windows Azure 佇列交易的成本會隨著佇列服務用戶端數目的增加而呈線性遞增,例如當擴充角色執行個體的數目或增加清除佇列執行緒數目時。為了說明未利用上述建議之解決方案設計的潛在成本影響,我們將會提供以具體數字為後盾的範例。
低效率設計的成本影響
如果解決方案架構設計人員未實作相關的最佳化,上面描述的計費系統架構可能會在部署解決方案並於 Windows Azure 平台上執行之後,產生過多的營運費用。本節將描述可能發生過多費用的原因。
如同案例定義中所述,商業交易資料會定期送達。不過,假設解決方案在標準 8 小時的工作日期間,只有 25% 的時間忙著處理工作負載。當沒有任何交易通過系統時,這樣會產生 6 小時 (8 小時 * 75%) 的「閒置時間」。此外,此解決方案在每天非上班時間的 16 小時期間將不會收到任何資料。
在一共 22 小時的閒置期間內,此解決方案依然會嘗試執行清除佇列工作,因為它無法明確得知新資料何時送達。在這段期間內,每個個別清除佇列執行緒最多會針對輸入佇列執行 79,200 筆交易 (22 小時 * 60 分鐘 * 每分鐘 60 筆交易),假設預設輪詢間隔為 1 秒。
如同之前所述,Windows Azure 平台中的計價模型是根據個別「儲存體交易」。儲存體交易是使用者應用程式為了新增、讀取、更新或刪除儲存體資料所提出的要求。撰寫這份白皮書時,在 10,000 筆交易中,儲存體交易的計費費率為 $0.01 (不考量任何促銷優惠或特殊價格協議)。
重要事項 |
|---|
| 當計算佇列交易數目時,請記得將單一訊息放在佇列中會算成 1 筆交易,而取用訊息則通常是 2 個步驟的程序,其中牽涉到擷取,然後是從佇列移除訊息的要求。因此,成功的清除佇列作業將會吸引兩筆儲存體交易。請注意,即使清除佇列要求並未擷取任何資料,依然算成可計費的交易。 |
上述案例的單一清除佇列執行緒所產生的儲存體交易會在每月帳單中增加 $2.38 美元 (79,200 / 10,000 * $0.01 * 30 天)。相較之下,200 個清除佇列執行緒 (或是 200 個背景工作角色執行個體中的 1 個清除佇列執行緒) 每個月會增加 $457.20 美元的成本。這是解決方案未執行任何計算時所發生的成本,此時解決方案只負責檢查佇列,看看是否有任何可用的工作項目。上述範例相當抽象,因為沒有人會以這種方式實作服務,這也是執行接下來描述的最佳化工作極為重要的原因。
去除過多延遲的最佳作法
若要將佇列架構 Windows Azure 傳訊解決方案的效能最佳化,其中一個方法是使用 Windows Azure Service Bus 所提供的發行/訂閱傳訊層,如本節所述。
在這個方法中,開發人員必須將焦點放在建立輪詢和即時發送架構通知的組合,好讓接聽程式訂閱在某些情況下所引發的通知事件 (觸發程序),以指示新的工作負載被放在佇列中。此方法會使用用於分派通知的發行/訂閱傳訊層來增強傳統佇列輪詢迴圈。
在複雜分散式系統中,這種方法需要使用 Message Bus 或「訊息導向中介軟體」,以確保通知可以鬆散偶合的方式可靠地轉送給一個或多個訂閱者。Windows Azure Service Bus 是用來處理 Windows Azure 上執行與內部部署執行的鬆散偶合分散式應用程式服務之間之傳訊需求的自然選擇。它也非常適合 Message Bus 架構,此架構將會啟用與佇列架構通訊有關之處理序之間的通知交換。
從事佇列架構訊息交換的處理序可能會利用以下模式:
具體而言,因為它與佇列服務發行者和訂閱者之間的互動有關,所以適用於 Windows Azure 角色執行個體之間通訊的原則同樣符合以發送為基礎之通知訊息交換的大部分要求。我們已經在如何使用 Windows Azure Service Bus 簡化及擴展角色之間的通訊中涵蓋這些基本面。
重要事項 |
|---|
| Windows Azure Service Bus 的使用受限於計價模型,計價模型會考量針對 Message Bus 傳訊實體 (例如佇列或主題) 執行的傳訊作業數量。 因此,執行成本效益分析來評估在特定架構中引進 Service Bus 的利弊非常重要。除此之外,也值得評估引進以 Service Bus 為基礎的通知分派層是否會真的導致成本降低,因而證明投資與額外的開發工作是值得的。 如需有關 Service Bus 計價模型的詳細資訊,請參閱 Windows Azure 平台常見問題集的相關章節。 |
雖然使用發行/訂閱傳訊層來處理對延遲的影響相當輕鬆,您可以使用動態 (彈性) 調整來進一步實現成本的降低,如下一節所述。
動態調整的最佳作法
Windows Azure 平台讓客戶可以前所未有的快速和輕鬆方式向上和向下延展。能夠根據易變的工作負載和變動的流量進行調整是雲端平台的其中一個主要價值主張。這表示,「延展性」不再是一個耗費成本的 IT 詞彙而已,而是一個現成的功能,使用者可以在架構良好的雲端解決方案中視需要以程式設計方式加以啟用。
「動態調整」是給定解決方案為了配合變動的工作負載而進行調整的技術性功能,其調整方式是在執行階段增加及減少工作能力和處理能力。Windows Azure 平台原本就可透過佈建分散式運算基礎結構來支援動態調整 (可以視需要在此基礎結構上購買運算時數)。
區分 Windows Azure 平台上的以下兩種動態調整類型非常重要:
-
角色執行個體調整指的是新增和移除其他 Web 或背景工作角色執行個體來處理時間點的工作負載。這通常包含變更服務組態中的執行個體計數。提高執行個體計數將會造成 Windows Azure 執行階段啟動新的執行個體,而減少執行個體計數則會讓它關閉執行中的執行個體。
-
處理序 (執行緒) 調整指的是根據給定角色執行個體中的處理中執行緒來維護足夠的容量,維護的方式是根據目前的工作負載向上和向下調整執行緒的數目。
佇列架構傳訊解決方案中的動態調整將會吸引以下一般建議的組合:
-
監視關鍵效能指標包括 CPU 使用量、佇列深度、回應時間和訊息處理延遲。
-
動態增加或減少角色執行個體數目可處理工作負載的尖峰,不論是可預測還是無法預測。
-
以程式設計方式擴充及修剪處理中執行緒的數目可配合給定角色執行個體處理之變動負載條件來進行調整。
-
同時分割和處理細部工作負載,它會在 .NET Framework 4 中使用工作平行程式庫。
-
在解決方案中維護有效的容量,並且具有高易變性的工作負載,以便在突然出現尖峰時能夠加以處理,而不用負擔設定其他執行個體的成本。
服務管理 API 可讓 Windows Azure 託管服務能夠藉由在執行階段變更部署組態來修改其執行中角色執行個體的數目。
附註 |
|---|
| 在典型的訂閱中,Windows Azure 小型運算執行個體數目上限 (或是以核心數目計算時,則是其他規模的運算執行個體的同等數目) 預設限制為 20。提高這個配額的所有要求都應該由 Windows Azure 支援團隊提出。如需詳細資訊,請參閱 Windows Azure 平台常見問題集。 |
角色執行個體計數的動態調整不見得是處理負載尖峰的最適合的選擇。例如,新的角色執行個體需要花幾秒鐘的時間來向上微調,而且目前沒有提供有關向上微調持續時間的任何 SLA 度量。不過,解決方案可能只需要增加工作者執行緒的數目,以處理易變的工作負載增加。正在處理工作負載時,解決方案將會監視相關的負載度量,並判斷它是否需要動態減少或增加背景工作處理序的數目。
重要事項 |
|---|
| 目前,單一 Windows Azure 佇列的延展性目標「受限於」500 筆交易/秒。如果應用程式嘗試超出此目標,例如,透過從執行數百個清除佇列執行緒的多個角色執行個體執行佇列作業,它可能會從儲存體服務產生 HTTP 503「伺服器忙碌」的回應。發生這種狀況時,應用程式應該使用指數型撤退延遲演算法來實作重試機制。不過,如果定期出現 HTTP 503 錯誤,建議您使用多個佇列並且實作資料分割架構策略,橫跨多個佇列進行調整。 |
在許多情況下,自動調整背景工作處理序是個別角色執行個體的責任。相反地,角色執行個體的調整通常牽涉到解決方案架構的中央元素,此元素負責監視效能度量並採取適當的調整動作。下圖描繪一個稱為「動態調整代理程式」的服務元件,此元件會收集及分析負載度量,以判斷它是否需要佈建新的執行個體或是讓閒置的執行個體退役。
請注意,此調整代理程式服務可以部署為 Windows Azure 上執行的背景工作角色或是部署為內部部署服務。不論部署拓撲為何,此服務都能夠存取 Windows Azure 佇列。
若要實作動態調整功能,請考慮使用 Microsoft 企業程式庫自動調整應用程式區塊,此區塊會在 Windows Azure 上執行的解決方案中啟用自動調整行為。此自動調整應用程式區塊會提供在 Windows Azure 應用程式中定義和監視自動調整所需的所有功能。
現在我們已涵蓋延遲影響、儲存體交易成本和動態調整需求,現在是將我們的建議合併到技術實作中的好時機。
技術實作
在之前的章節中,我們檢驗了歸於設計良好之傳訊架構的主要特性,此架構是根據 Windows Azure 佇列儲存體的佇列。我們已經探討了三個主要焦點區域,這些區域有助於減少處理延遲、最佳化儲存體交易成本,以及改善對變動工作負載的回應能力。
本章節的目的是在提供起點來協助 Windows Azure 開發人員從程式設計觀點實作本白皮書所參考的一些模式。
附註 |
|---|
| 本章節將焦點放在建立可自動調整的佇列接聽程式,此接聽程式同時支援以提取為基礎的模型和以發送為基礎的模型。如需動態調整中角色執行個體層級的進階技術,請參閱企業程式庫自動調整應用程式區塊。 此外,為了簡短起見,我們只將焦點放在一些核心功能元素,並且省略底下程式碼範例中的許多支援的基礎結構程式碼來避免不必要的複雜度。我們也要澄清,底下所討論的技術實作並不是給定問題空間的唯一解決方案。此實作是當做一個起點,開發人員可以從這個起點來衍生更有質感的解決方案。 |
從這裡開始,本白皮書會將焦點放在實作上面討論的模式所需的原始程式碼。
建立一般佇列接聽程式
首先,我們會定義將由佇列接聽程式元件實作的合約,此元件是由背景工作角色所主控,並且會接聽 Windows Azure 佇列。
/// Defines a contract that must be implemented by an extension responsible for listening on a Windows Azure queue.
public interface ICloudQueueServiceWorkerRoleExtension
{
/// Starts a multi-threaded queue listener that uses the specified number of dequeue threads.
void StartListener(int threadCount);
/// Returns the current state of the queue listener to determine point-in-time load characteristics.
CloudQueueListenerInfo QueryState();
/// Gets or sets the batch size when performing dequeue operation against a Windows Azure queue.
int DequeueBatchSize { get; set; }
/// Gets or sets the default interval that defines how long a queue listener will be idle for between polling a queue.
TimeSpan DequeueInterval { get; set; }
/// Defines a callback delegate which will be invoked whenever the queue is empty.
event WorkCompletedDelegate QueueEmpty;
}
QueueEmpty 事件是為了供主機使用。如果佇列是空的,它便會為主機提供用來控制佇列接聽程式行為的機制。各自的事件委派定義如下:
/// <summary> /// Defines a callback delegate which will be invoked whenever an unit of work has been completed and the worker is /// requesting further instructions as to next steps. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="idleCount">The value indicating how many times the worker has been idle.</param> /// <param name="delay">Time interval during which the worker is instructed to sleep before performing next unit of work.</param> /// <returns>A flag indicating that the worker should stop processing any further units of work and must terminate.</returns> public delegate bool WorkCompletedDelegate(object sender, int idleCount, out TimeSpan delay);
如果接聽程式可以使用泛型來操作 (而不是使用類似 CloudQueueMessage 的「裸機」SDK 類別),處理佇列項目會更容易。因此,我們會定義將由佇列接聽程式實作的一個新介面,此介面能夠支援以泛型為基礎的佇列存取方式:
/// <summary>
/// Defines a contract that must be supported by an extension that implements a generics-aware queue listener.
/// </summary>
/// <typeparam name="T">The type of queue item data that will be handled by the queue listener.</typeparam>
public interface ICloudQueueListenerExtension<T> : ICloudQueueServiceWorkerRoleExtension, IObservable<T>
{
}
請注意,我們也啟用了能夠感知泛型的接聽程式,以便透過實作觀察器設計模式將佇列項目發送給一個或多個訂閱者,其實作方式是利用可在 .NET Framework 4 中使用的 IObservable<T> 介面。
我們想要保留實作 ICloudQueueListenerExtension<T> 介面之元件的單一執行個體。不過,我們需要能夠執行多個清除佇列執行緒 (背景工作處理序或是簡單的工作)。因此,我們在佇列接聽程式元件中新增了多執行緒之清除佇列邏輯的支援。這也是我們利用工作平行程式庫 (TPL) 的地方。StartListener 方法將負責向上微調指定之數目的清除佇列執行緒,如下所示:
/// <summary>
/// Starts the specified number of dequeue tasks.
/// </summary>
/// <param name="threadCount">The number of dequeue tasks.</param>
public void StartListener(int threadCount)
{
Guard.ArgumentNotZeroOrNegativeValue(threadCount, "threadCount");
// The collection of dequeue tasks needs to be reset on each call to this method.
if (this.dequeueTasks.IsAddingCompleted)
{
this.dequeueTasks = new BlockingCollection<Task>(this.dequeueTaskList);
}
for (int i = 0; i < threadCount; i++)
{
CancellationToken cancellationToken = this.cancellationSignal.Token;
CloudQueueListenerDequeueTaskState<T> workerState = new CloudQueueListenerDequeueTaskState<T>(Subscriptions, cancellationToken, this.queueLocation, this.queueStorage);
// Start a new dequeue task and register it in the collection of tasks internally managed by this component.
this.dequeueTasks.Add(Task.Factory.StartNew(DequeueTaskMain, workerState, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default));
}
// Mark this collection as not accepting any more additions.
this.dequeueTasks.CompleteAdding();
}
DequeueTaskMain 方法會實作清除佇列執行緒的功能主體。其主要作業如下:
/// <summary>
/// Implements a task performing dequeue operations against a given Windows Azure queue.
/// </summary>
/// <param name="state">An object containing data to be used by the task.</param>
private void DequeueTaskMain(object state)
{
CloudQueueListenerDequeueTaskState<T> workerState = (CloudQueueListenerDequeueTaskState<T>)state;
int idleStateCount = 0;
TimeSpan sleepInterval = DequeueInterval;
try
{
// Run a dequeue task until asked to terminate or until a break condition is encountered.
while (workerState.CanRun)
{
try
{
var queueMessages = from msg in workerState.QueueStorage.Get<T>(workerState.QueueLocation.QueueName, DequeueBatchSize, workerState.QueueLocation.VisibilityTimeout).AsParallel() where msg != null select msg;
int messageCount = 0;
// Process the dequeued messages concurrently by taking advantage of the above PLINQ query.
queueMessages.ForAll((message) =>
{
// Reset the count of idle iterations.
idleStateCount = 0;
// Notify all subscribers that a new message requires processing.
workerState.OnNext(message);
// Once successful, remove the processed message from the queue.
workerState.QueueStorage.Delete<T>(message);
// Increment the number of processed messages.
messageCount++;
});
// Check whether or not we have done any work during this iteration.
if (0 == messageCount)
{
// Increment the number of iterations when we were not doing any work (e.g. no messages were dequeued).
idleStateCount++;
// Call the user-defined delegate informing that no more work is available.
if (QueueEmpty != null)
{
// Check if the user-defined delegate has requested a halt to any further work processing.
if (QueueEmpty(this, idleStateCount, out sleepInterval))
{
// Terminate the dequeue loop if user-defined delegate advised us to do so.
break;
}
}
// Enter the idle state for the defined interval.
Thread.Sleep(sleepInterval);
}
}
catch (Exception ex)
{
if (ex is OperationCanceledException)
{
throw;
}
else
{
// Offload the responsibility for handling or reporting the error to the external object.
workerState.OnError(ex);
// Sleep for the specified interval to avoid a flood of errors.
Thread.Sleep(sleepInterval);
}
}
}
}
finally
{
workerState.OnCompleted();
}
}
在與 DequeueTaskMain 方法實作相關的層面上,有幾個要點值得注意。
首先,在分派訊息以供處理時,我們會利用平行 LINQ (PLINQ)。這裡的 PLINQ 的主要優點是為了加速訊息的處理,加速的方式是以盡可能平行的方式,在多個處理器的不同工作者執行緒上執行查詢委派。
附註 |
|---|
| 因為查詢平行化是由 PLINQ 在內部所管理,所以不保證 PLINQ 將會利用一個以上的核心來進行工作平行化。如果 PLINQ 判斷平行化的負擔將會減緩查詢,它可能會循序執行查詢。為了從 PLINQ 獲益,查詢中的工作總數必須夠大,才能從執行緒集區上的工作排程負擔中獲益。 |
其次,我們不會一次提取單一訊息。不過,我們會要求佇列服務 API 從佇列中擷取特定數目的訊息。這是由傳遞給 Get<T> 方法的 DequeueBatchSize 參數所驅動。當我們進入整體解決方案中所實作的儲存體抽象層時,這個參數會移交給佇列服務 API 方法。此外,我們會執行安全性檢查,以確保批次大小不會超過 API 支援的大小上限。實作方式如下:
/// This class provides reliable generics-aware access to the Windows Azure Queue storage.
public sealed class ReliableCloudQueueStorage : ICloudQueueStorage
{
/// The maximum batch size supported by Queue Service API in a single Get operation.
private const int MaxDequeueMessageCount = 32;
/// Gets a collection of messages from the specified queue and applies the specified visibility timeout.
public IEnumerable<T> Get<T>(string queueName, int count, TimeSpan visibilityTimeout)
{
Guard.ArgumentNotNullOrEmptyString(queueName, "queueName");
Guard.ArgumentNotZeroOrNegativeValue(count, "count");
try
{
var queue = this.queueStorage.GetQueueReference(CloudUtility.GetSafeContainerName(queueName));
IEnumerable<CloudQueueMessage> queueMessages = this.retryPolicy.ExecuteAction<IEnumerable<CloudQueueMessage>>(() =>
{
return queue.GetMessages(Math.Min(count, MaxDequeueMessageCount), visibilityTimeout);
});
// ... There is more code after this point ...
最後,我們不會無限期地執行清除佇列工作。我們已經佈建實作為 QueueEmpty 事件的明確檢查點,每當佇列變空的時候就會引發這個事件。然後,我們會查詢 QueueEmpty 事件處理常式,以判斷它是否允許我們完成清除佇列工作的執行。QueueEmpty 事件處理常式的良好設計實作方式將可支援「自動向下調整」功能,如下列章節所說明。
自動向下調整清除佇列工作
QueueEmpty 事件處理常式有兩個用途。首先,它負責提供回應給來源清除佇列工作,以指示它在給定期間進入休眠狀態 (如同事件委派的 delay 輸出參數所定義)。其次,它會向清除佇列工作指示,它是否必須正常關閉自己 (如同布林值傳回參數所規定)。
QueueEmpty 事件處理常式的下列實作方式會解決本白皮書稍早所強調的兩個挑戰。它會計算隨機的指數型撤退間隔,並告訴清除佇列工作要以指數方式增加佇列輪詢要求之間的延遲。請注意,如同解決方案所設定,撤退延遲將不會超過 1 秒鐘,因為當自動調整的實作已經夠好時,輪詢之間就不需要長的延遲。此外,它會查詢佇列接聽程式狀態,以判斷使用中清除佇列工作的數目。萬一這個數目超過 1,事件處理常式會建議起始清除佇列工作完成其輪詢迴圈,前提是撤退間隔也到達其指定的最大值。否則,清除佇列工作將不會終止,佇列接聽程式的每一個執行個體都會剛好留下 1 個輪詢執行緒。如同之前所說明,這個方法有助於減少儲存體交易數目,因此也會降低交易成本。
private bool HandleQueueEmptyEvent(object sender, int idleCount, out TimeSpan delay)
{
// The sender is an instance of the ICloudQueueServiceWorkerRoleExtension, we can safely perform type casting.
ICloudQueueServiceWorkerRoleExtension queueService = sender as ICloudQueueServiceWorkerRoleExtension;
// Find out which extension is responsible for retrieving the worker role configuration settings.
IWorkItemProcessorConfigurationExtension config = Extensions.Find<IWorkItemProcessorConfigurationExtension>();
// Get the current state of the queue listener to determine point-in-time load characteristics.
CloudQueueListenerInfo queueServiceState = queueService.QueryState();
// Set up the initial parameters, read configuration settings.
int deltaBackoffMs = 100;
int minimumIdleIntervalMs = Convert.ToInt32(config.Settings.MinimumIdleInterval.TotalMilliseconds);
int maximumIdleIntervalMs = Convert.ToInt32(config.Settings.MaximumIdleInterval.TotalMilliseconds);
// Calculate a new sleep interval value that will follow a random exponential back-off curve.
int delta = (int)((Math.Pow(2.0, (double)idleCount) - 1.0) * (new Random()).Next((int)(deltaBackoffMs * 0.8), (int)(deltaBackoffMs * 1.2)));
int interval = Math.Min(minimumIdleIntervalMs + delta, maximumIdleIntervalMs);
// Pass the calculated interval to the dequeue task to enable it to enter into a sleep state for the specified duration.
delay = TimeSpan.FromMilliseconds((double)interval);
// As soon as interval reaches its maximum, tell the source dequeue task that it must gracefully terminate itself
// unless this is a last deqeueue task. If so, we are not going to keep it running and continue polling the queue.
return delay.TotalMilliseconds >= maximumIdleIntervalMs && queueServiceState.ActiveDequeueTasks > 1;
}
上述的「清除佇列工作向下調整」功能可以在較高的層級解釋如下:
-
每當佇列中有任何東西時,清除佇列工作都會確保能夠盡快處理工作負載。從佇列清除佇列訊息的要求之間將不會有任何延遲。
-
一旦來源佇列變空時,每項清除佇列工作都將引發 QueueEmpty 事件。
-
QueueEmpty 事件處理常式將會計算隨機的指數型撤退延遲,並指示清除佇列工作在給定的間隔暫停其活動。
-
清除佇列工作將持續在計算的間隔輪詢來源佇列,直到閒置持續時間超出其允許的最大值為止。
-
當到達最大閒置間隔而且假定來源佇列依然是空的時,所有使用中清除佇列工作都將正常自行關閉,這不會全部同時發生,因為清除佇列工作會在撤退演算法的不同時間點撤退。
-
在某個時間點,只會有一個使用中清除佇列工作等待工作。因此,將不會針對佇列發生任何閒置輪詢交易,除非是該單一工作進行的交易。
為了闡述收集時間點負載特性的程序,相關的原始程式碼成品值得一提。首先,有一個結構保存相關的度量,這些度量會測量正在套用至解決方案的負載結果。為求簡單,我們已併入一小部分的度量,這些度量將會進一步用於範例程式碼。
/// Implements a structure containing point-in-time load characteristics for a given queue listener.
public struct CloudQueueListenerInfo
{
/// Returns the approximate number of items in the Windows Azure queue.
public int CurrentQueueDepth { get; internal set; }
/// Returns the number of dequeue tasks that are actively performing work or waiting for work.
public int ActiveDequeueTasks { get; internal set; }
/// Returns the maximum number of dequeue tasks that were active at a time.
public int TotalDequeueTasks { get; internal set; }
}
接著,有一個方法是由佇列接聽程式所實作,此接聽程式會傳回其負載度量,如以下範例所述:
/// Returns the current state of the queue listener to determine point-in-time load characteristics.
public CloudQueueListenerInfo QueryState()
{
return new CloudQueueListenerInfo()
{
CurrentQueueDepth = this.queueStorage.GetCount(this.queueLocation.QueueName),
ActiveDequeueTasks = (from task in this.dequeueTasks where task.Status != TaskStatus.Canceled && task.Status != TaskStatus.Faulted && task.Status != TaskStatus.RanToCompletion select task).Count(),
TotalDequeueTasks = this.dequeueTasks.Count
};
}
自動向上調整清除佇列工作
在上一節中,我們介紹了將使用中清除佇列工作數目減少為單一執行個體的功能,以便將閒置交易對於儲存體作業成本的影響降到最低。在這一節,我們即將逐步討論一個對照的範例,我們會在此範例中實作「自動向上調整」功能,在需要時帶回處理功能。
首先,我們會定義一個事件委派,此委派有助於追蹤從空的到不是空的佇列的狀態轉換,以便觸發相關的動作:
/// <summary> /// Defines a callback delegate which will be invoked whenever new work arrived to a queue while the queue listener was idle. /// </summary> /// <param name="sender">The source of the event.</param> public delegate void WorkDetectedDelegate(object sender);
然後我們會擴充 ICloudQueueServiceWorkerRoleExtension 介面的原始定義來併入新的事件,每當佇列接聽程式偵測到新的工作項目,特別是當佇列深度從零變成任何正值時,都將引發這個事件:
public interface ICloudQueueServiceWorkerRoleExtension
{
// ... The other interface members were omitted for brevity. See the previous code snippets for reference ...
// Defines a callback delegate to be invoked whenever a new work has arrived to a queue while the queue listener was idle.
event WorkDetectedDelegate QueueWorkDetected;
}
此外,我們會判斷這類事件在佇列接聽程式的程式碼中引發的適當地方。我們即將從 DequeueTaskMain 方法內實作的清除佇列迴圈中引發 QueueWorkDetected 事件,該方法必須依照以下方式擴充:
public class CloudQueueListenerExtension<T> : ICloudQueueListenerExtension<T>
{
// An instance of the delegate to be invoked whenever a new work has arrived to a queue while the queue listener was idle.
public event WorkDetectedDelegate QueueWorkDetected;
private void DequeueTaskMain(object state)
{
CloudQueueListenerDequeueTaskState<T> workerState = (CloudQueueListenerDequeueTaskState<T>)state;
int idleStateCount = 0;
TimeSpan sleepInterval = DequeueInterval;
try
{
// Run a dequeue task until asked to terminate or until a break condition is encountered.
while (workerState.CanRun)
{
try
{
var queueMessages = from msg in workerState.QueueStorage.Get<T>(workerState.QueueLocation.QueueName, DequeueBatchSize, workerState.QueueLocation.VisibilityTimeout).AsParallel() where msg != null select msg;
int messageCount = 0;
// Check whether or not work items arrived to a queue while the listener was idle.
if (idleStateCount > 0 && queueMessages.Count() > 0)
{
if (QueueWorkDetected != null)
{
QueueWorkDetected(this);
}
}
// ... The rest of the code was omitted for brevity. See the previous code snippets for reference ...
在最後一個步驟中,我們會針對 QueueWorkDetected 事件提供處理常式。此事件處理常式的實作將會由具現化及主控佇列接聽程式的元件所提供。在我們的案例中,這是背景工作角色。負責具現化及實作事件處理常式的程式碼由以下部分所組成:
public class WorkItemProcessorWorkerRole : RoleEntryPoint
{
// Called by Windows Azure to initialize the role instance.
public override sealed bool OnStart()
{
// ... There is some code before this point ...
// Instantiate a queue listener for the input queue.
var inputQueueListener = new CloudQueueListenerExtension<XDocument>(inputQueueLocation);
// Configure the input queue listener.
inputQueueListener.QueueEmpty += HandleQueueEmptyEvent;
inputQueueListener.QueueWorkDetected += HandleQueueWorkDetectedEvent;
inputQueueListener.DequeueBatchSize = configSettingsExtension.Settings.DequeueBatchSize;
inputQueueListener.DequeueInterval = configSettingsExtension.Settings.MinimumIdleInterval;
// ... There is more code after this point ...
}
// Implements a callback delegate to be invoked whenever a new work has arrived to a queue while the queue listener was idle.
private void HandleQueueWorkDetectedEvent(object sender)
{
// The sender is an instance of the ICloudQueueServiceWorkerRoleExtension, we can safely perform type casting.
ICloudQueueServiceWorkerRoleExtension queueService = sender as ICloudQueueServiceWorkerRoleExtension;
// Get the current state of the queue listener to determine point-in-time load characteristics.
CloudQueueListenerInfo queueServiceState = queueService.QueryState();
// Determine the number of queue tasks that would be required to handle the workload in a queue given its current depth.
int dequeueTaskCount = GetOptimalDequeueTaskCount(queueServiceState.CurrentQueueDepth);
// If the dequeue task count is less than computed above, start as many dequeue tasks as needed.
if (queueServiceState.ActiveDequeueTasks < dequeueTaskCount)
{
// Start the required number of dequeue tasks.
queueService.StartListener(dequeueTaskCount - queueServiceState.ActiveDequeueTasks);
}
} // ... There is more code after this point ...
根據上述範例,我們發現 GetOptimalDequeueTaskCount 方法值得深入探究。這個方法負責計算將視為最適合處理佇列中工作負載的清除佇列工作數目。此方法被叫用時,應該判斷 (透過任何適當的決策機制) 佇列接聽程式需要多少「馬力」,以便處理在等候或是預期抵達給定佇列的工作數量。
例如,開發人員可以採用簡單的方法,並將一組靜態規則直接內嵌到 GetOptimalDequeueTaskCount 方法中。透過已知的輸送量及佇列基礎結構的延展性特性、平均處理延遲、裝載大小和其他相關輸入,這一組規則會採用樂觀的角度,並決定最佳清除佇列工作計數。
在下列範例中,我們使用一個故意過度簡化的技術來判斷清除佇列工作數目:
/// <summary>
/// Returns the number of queue tasks that would be required to handle the workload in a queue given its current depth.
/// </summary>
/// <param name="currentDepth">The approximate number of items in the queue.</param>
/// <returns>The optimal number of dequeue tasks.</returns>
private int GetOptimalDequeueTaskCount(int currentDepth)
{
if (currentDepth < 100) return 10;
if (currentDepth >= 100 && currentDepth < 1000) return 50;
if (currentDepth >= 1000) return 100;
// Return the minimum acceptable count.
return 1;
}
我們要再重申一次,上述的範例程式碼無法適合所有情況使用。更理想的解決方案會是叫用可在外部設定及管理的規則,該規則會執行必要的計算。
此時,我們便擁有一個佇列接聽程式的工作原型,能夠隨著工作負載的變動而自動向上和向下調整自己。也許在最後一個步驟中,必須透過在處理中隨著變動的負載自行調整的功能,讓它更為豐富。您可以套用在新增 QueueWorkDetected 事件的支援時所遵循的相同模式來新增此功能。
現在,讓我們將焦點切換到另一個重要的最佳化工作,此工作將有助於減少佇列接聽程式的延遲。
針對零延遲的清除佇列實作發行/訂閱層
在這一節,我們即將使用以發送為基礎的通知機制 (建立於 Service Bus 單向多點傳送功能之上),增強上述的佇列接聽程式實作。此通知機制負責觸發一個事件,告知佇列接聽程式開始執行清除佇列工作。這個方法有助於避免輪詢佇列來檢查新的訊息,因此也會去除相關的延遲。
首先,我們會定義一個觸發程序事件,當新的工作負載存到佇列時,我們的佇列接聽程式將會收到這個事件:
/// Implements a trigger event indicating that a new workload was put in a queue.
[DataContract(Namespace = WellKnownNamespace.DataContracts.Infrastructure)]
public class CloudQueueWorkDetectedTriggerEvent
{
/// Returns the name of the storage account on which the queue is located.
[DataMember]
public string StorageAccount { get; private set; }
/// Returns a name of the queue where the payload was put.
[DataMember]
public string QueueName { get; private set; }
/// Returns a size of the queue's payload (e.g. the size of a message or the number of messages in a batch).
[DataMember]
public long PayloadSize { get; private set; }
// ... The constructor was omitted for brevity ...
}
接下來,我們會啟用佇列接聽程式實作,當做接收觸發程序事件的訂閱者。第一個步驟是針對 CloudQueueWorkDetectedTriggerEvent 事件將佇列接聽程式定義為觀察器:
/// Defines a contract that must be implemented by an extension responsible for listening on a Windows Azure queue.
public interface ICloudQueueServiceWorkerRoleExtension : IObserver<CloudQueueWorkDetectedTriggerEvent>
{
// ... The body is omitted as it was supplied in previous examples ...
}
第二個步驟是實作 IObserver<T> 介面中所定義的 OnNext 方法。此方法會由提供者呼叫,以通知觀察器有關新的事件的消息:
public class CloudQueueListenerExtension<T> : ICloudQueueListenerExtension<T>
{
// ... There is some code before this point ...
/// <summary>
/// Gets called by the provider to notify this queue listener about a new trigger event.
/// </summary>
/// <param name="e">The trigger event indicating that a new payload was put in a queue.</param>
public void OnNext(CloudQueueWorkDetectedTriggerEvent e)
{
Guard.ArgumentNotNull(e, "e");
// Make sure the trigger event is for the queue managed by this listener, otherwise ignore.
if (this.queueLocation.StorageAccount == e.StorageAccount && this.queueLocation.QueueName == e.QueueName)
{
if (QueueWorkDetected != null)
{
QueueWorkDetected(this);
}
}
}
// ... There is more code after this point ...
}
如同上述範例所示,我們故意叫用之前的步驟所使用的相同事件委派。QueueWorkDetected 事件處理常式已經提供具現化最佳清除佇列工作數目所需的應用程式邏輯。因此在處理 CloudQueueWorkDetectedTriggerEvent 通知時,將會重複使用相同的事件處理常式。
如同之前的章節所述,在利用以發送為基礎的通知時,我們不需要維護持續執行中的清除佇列工作。因此,我們可以將每個佇列接聽程式執行個體的佇列工作數目減少為零,並使用通知機制,在佇列接收工作項目時具現化清除佇列工作。為了確保我們不會執行任何閒置的清除佇列工作,需要在 QueueEmpty 事件處理常式中直接進行以下修改:
private bool HandleQueueEmptyEvent(object sender, int idleCount, out TimeSpan delay)
{
// ... There is some code before this point ...
// As soon as interval reaches its maximum, tell the source dequeue task that it must gracefully terminate itself.
return delay.TotalMilliseconds >= maximumIdleIntervalMs;
}
總而言之,我們不再偵測是否有單一使用中清除佇列工作依然存在。修改過的 QueueEmpty 事件處理常式結果只會考量超出最大閒置間隔的事實,在發生這個狀況後,所有使用中清除佇列工作都將關閉。
為了接收 CloudQueueWorkDetectedTriggerEvent 通知,我們會利用發行/訂閱模型,此模型會實作為 Windows Azure 角色執行個體之間鬆散偶合的傳訊。基本上,我們會採用相同的角色之間通訊層,並依照以下方式處理內送事件:
public class InterRoleEventSubscriberExtension : IInterRoleEventSubscriberExtension
{
// ... Some code here was omitted for brevity. See the corresponding guidance on Windows Azure CAT team blog for reference ...
public void OnNext(InterRoleCommunicationEvent e)
{
if (this.owner != null && e.Payload != null)
{
// ... There is some code before this point ...
if (e.Payload is CloudQueueWorkDetectedTriggerEvent)
{
HandleQueueWorkDetectedTriggerEvent(e.Payload as CloudQueueWorkDetectedTriggerEvent);
return;
}
// ... There is more code after this point ...
}
}
private void HandleQueueWorkDetectedTriggerEvent(CloudQueueWorkDetectedTriggerEvent e)
{
Guard.ArgumentNotNull(e, "e");
// Enumerate through registered queue listeners and relay the trigger event to them.
foreach (var queueService in this.owner.Extensions.FindAll<ICloudQueueServiceWorkerRoleExtension>())
{
// Pass the trigger event to a given queue listener.
queueService.OnNext(e);
}
}
}
多點傳送 CloudQueueWorkDetectedTriggerEvent 類別中定義的觸發程序事件是發行者 (也就是將工作項目存到佇列上的元件) 的最終責任。此事件可以在第一個工作項目加入佇列之前觸發,或是在最後一個項目放入佇列之後觸發。在底下的範例中,我們會在完成將工作項目放入輸入佇列後立即發行觸發程序事件:
public class ProcessInitiatorWorkerRole : RoleEntryPoint
{
// The instance of the role extension which provides an interface to the inter-role communication service.
private volatile IInterRoleCommunicationExtension interRoleCommunicator;
// ... Some code here was omitted for brevity. See the corresponding guidance on Windows Azure CAT team blog for reference ...
private void HandleWorkload()
{
// Step 1: Receive compute-intensive workload.
// ... (code was omitted for brevity) ...
// Step 2: Enqueue work items into the input queue.
// ... (code was omitted for brevity) ...
// Step 3: Notify the respective queue listeners that they should expect work to arrive.
// Create a trigger event referencing the queue into which we have just put work items.
var trigger = new CloudQueueWorkDetectedTriggerEvent("MyStorageAccount", "InputQueue");
// Package the trigger into an inter-role communication event.
var interRoleEvent = new InterRoleCommunicationEvent(CloudEnvironment.CurrentRoleInstanceId, trigger);
// Publish inter-role communication event via the Service Bus one-way multicast.
interRoleCommunicator.Publish(interRoleEvent);
}
}
我們建立的佇列接聽程式能夠支援多執行緒、自動調整以及以發送為基礎的通知,現在是合併與 Windows Azure 平台上設計佇列架構傳訊解決方案有關之所有建議的時間。
結論
若要讓 Windows Azure 平台上執行之佇列架構傳訊解決方案的效率和成本效益最大化,解決方案架構設計人員和開發人員應該考慮下列建議。
您身為解決方案架構設計人員,應該進行以下作業:
-
佈建以佇列為基礎的傳訊架構,此架構會在雲端架構或混合式解決方案的不同層與服務之間使用 Windows Azure 佇列儲存體服務,以進行高等級的非同步通訊。
-
建議使用資料分割佇列架構,以擴充到每秒 500 筆交易之外。
-
了解 Windows Azure 計價模型的基本面,並透過一系列的最佳作法和設計模式最佳化解決方案來降低交易成本。
-
藉由佈建一個可配合易變和波動的工作負載進行調整的架構來考慮動態調整需求。
-
運用適當的自動調整技術和方法,彈性地擴充和縮小電腦能力,以進一步地最佳化營運費用。
-
藉由相依於 Windows Azure Service Bus 來進行即時的發送架構通知分派,以評估降低延遲的成本效益比。
您身為開發人員,應該進行以下作業:
-
設計一種傳訊解決方案,當從 Windows Azure 佇列儲存和擷取資料時,此解決方案會運用批次處理。
-
實作高效率的佇列接聽程式服務,以確保當佇列是空的時候,將由一個清除佇列執行緒的最大值輪詢佇列。
-
當佇列在一段很長的時間依然是空的時候,動態往下調整背景工作角色執行個體的數目。
-
實作應用程式特有的隨機指數型撤退演算法,以減少閒置佇列輪詢對於儲存體交易成本的影響。
-
採用適當的技術,以免在實作高度多執行緒的多個執行個體佇列發行者和取用者時,超出單一佇列的延展性目標。
-
運用強固的重試原則,在發行和取用 Windows Azure 佇列中的資料時,此原則將能夠處理各種不同的短暫狀況。
-
使用 Windows Azure Service Bus 提供的單向事件功能來支援以發送為基礎的通知,以減少延遲及改善以佇列為基礎的傳訊解決方案效能。
-
探索 .NET Framework 4 的新功能,例如 TPL、PLINQ 和觀察器模式,以最大化平行處理原則的程度、改善並行存取,並且簡化多執行緒服務的設計。
隨附的範例程式碼可從 MSDN Code Gallery 下載。此範例程式碼也包含所有必要的基礎結構元件,例如 Windows Azure 佇列服務適用的可感知泛型的抽象層,上述程式碼片段中並未提供這些元件。請注意,所有原始程式碼檔案都受到 Microsoft 公用授權所控管,如同對應的法律聲明所述。
其他資源/參考資料
如需本白皮書中所討論之主題的詳細資訊,請參閱下列文件:
-
Windows Azure 儲存體團隊部落格上的了解 Windows Azure 儲存體計費 – 頻寬、交易和容量文章。
-
MSDN Library 上的服務管理 API 文章。
-
Neil Mackenzie 的部落格上的關於 Windows Azure 中的服務管理 API 文章。
-
MSDN CodePlex 上的 Windows Azure 服務管理指令程式專案。
-
Windows Azure 儲存體團隊部落格上的 Windows Azure 儲存體抽象概念及其延展性目標。
-
Microsoft Research 的 eXtreme Computing Group 所發行的佇列讀取/寫入輸送量研究。
-
MSDN Code Gallery 上的 Azure 儲存體、Service Bus 和 Windows Azure SQL Database 處理架構的暫時性失敗專案。
-
MSDN Library 中的自動調整應用程式區塊。
-
Wely Lau 的部落格上的 Windows Azure 儲存體交易 - 揭露未預見的成本以及具成本效益之使用方式的秘訣文章。
建置日期:
附註
重要事項