2012-02-23 48 views
0

昨天當我們嘗試更換我們的分段< - >生產角色時,我們遇到了可怕的問題/體驗。Windows Azure升級<-->生產導致表存儲中的衝突和錯誤

這裏是我們的設置:

我們有一個workerrole從隊列中拿起消息。這些消息在角色上處理。 (表存儲插入,數據庫選擇等)。這可能需要每隊列消息1-3秒,具體取決於他需要創建多少表格存儲帖子。一切完成後,他將刪除該消息。

問題交換時:

當我們的分期項目上線我們的生產workerrole開始示數。

當角色想要處理隊列消息時,它給了'EntityAlreadyExists'錯誤的持續流。由於這些錯誤,隊列消息未被刪除。這導致隊列消息被放回到隊列中並返回到處理等。......

當查看這些隊列消息並分析將發生什麼事情時,我們看到它們實際已處理但未被刪除。

刪除這些錯誤消息時,問題還沒有結束。新的隊列消息沒有得到處理,而這些消息尚未處理,也沒有添加表存儲記錄,這聽起來很奇怪。

當刪除分段和生產並再次發佈到生產時,一切都開始正常工作。

可能的問題?

我們有litle 2不知道實際發生了什麼事。

  • 也許這兩個角色拿起相同的消息,一個發佈了帖子,一個發生了錯誤?
  • ... ???

可能的解決方案?

我們對如何解決這個「問題」有一些想法。

  • 讓毒訊息在系統中失效?當出隊計數超過X時,我們應該刪除該隊列消息或將其放入單獨的「poisonqueue」中。
  • 捕獲EntityAlreadyExists錯誤,只刪除該隊列消息或將其放入單獨的隊列中。
  • ... ????

的多重角色

我想我們都會有同樣的問題搭建多個角色時?

非常感謝。

編輯24/02/2012 - 額外的信息

  • 我們實際使用的的GetMessage()
  • 在隊列中的每個項目都是獨特的,將產生表存儲唯一的消息。關於該過程的更多信息:用戶發佈內容並且必須分發給某些其他用戶。從該用戶生成的消息將具有唯一的Id(guid)。該消息將被髮布到隊列中並由工作者角色提取。該消息分佈在多個其他表(分區鍵 - > UserId,rowkey - >某些時間戳記&中唯一的消息ID。因此幾乎沒有機會在相同的消息發佈在正常情況下
  • 不可見時間出可能是一個合乎邏輯的解釋,因爲有些消息可能被分配到喜歡10-20表。這意味着10-20插入不批選項。你可以設置或擴大這一可見時間出來?
  • 不刪除隊列消息,因爲有異常可能也是一個解釋,因爲我們沒有實現任何有毒消息故障轉移YET;)。
+0

最新的隊列實現允許您更改可見性超時。 http://msdn.microsoft.com/en-us/library/windowsazure/hh452234.aspx – hocho 2012-02-24 16:55:52

回答

1

你在處理雙重信息時顯然有錯。你的ID是唯一的,但這並不意味着該消息不會像一些場合兩次處理:

  1. 角色死亡,並與部分完成的工作,因此,該消息將在處理重新出現隊列
  2. 作用崩潰意外,因此消息放回隊列
  3. 的FC移植移動你的角色結束了,你不用代碼來處理這種情況,所以在消息隊列中結束了回來

在所有情況下,您都需要處理以下事實的代碼:該消息將重新出現。一種方法是使用DequeueCount屬性,並檢查消息從隊列中刪除並接收處理的次數。確保你有處理消息部分處理的代碼。

現在,交換過程中可能發生的情況是,當生產環境變爲暫存並且暫存生產時,它們都試圖接收相同的消息,因此它們基本上是相互競爭的,這可能並不壞因爲這是一種已知的模式,但無論如何,除非您殺死舊生產(暫存)接收到的用於處理且未完成的每條消息,最終返回到Queue中,並且您的新生產環境再次選取要處理的消息。沒有代碼邏輯來處理這種情況,並且一條消息被部分處理,表中的一些記錄就存在了,它開始引起你注意到的行爲。

2

無論分階段還是生產問題,使用處理有毒消息的機制都至關重要。我們已經在Azure隊列上實現了一個抽象層,一旦嘗試處理一些可配置的時間量,消息就會自動將消息轉移到毒性隊列中。

1

有幾種可能的原因:

你如何閱讀隊列消息?如果您正在執行Peek消息,那麼在消息被刪除之前,消息仍然可以被另一個角色實例(或您的分段環境)所看到。您希望確保您使用「獲取消息」,這樣消息纔可以被刪除。

在刪除郵件之前,您的第一個角色是否可能在完成郵件工作之後崩潰?這將導致消息再次變爲可見,並被另一個角色實例拾取。此時該消息將成爲一個毒害消息,這將導致您的實例不斷崩潰。

這個問題幾乎肯定與Staging vs Production無關,但很可能是由於有多個實例從同一個隊列中讀取而導致的。通過指定2個實例,或者通過將相同的代碼部署到2個不同的生產服務,或者通過使用2個實例在您的開發機器上本地運行代碼(仍指向Azure存儲),您可能會再現相同的問題。

一般來說,你確實需要處理中毒消息,所以你需要實現這個邏輯,但我會建議先解決這個問題的根本原因,否則你將在稍後遇到更多問題。

+0

感謝您的評論。我已編輯我的帖子,更多的信息:) – 2012-02-24 07:58:20

1

不知道你的工作角色實際上在做什麼我在這裏做一個猜測,但這聽起來像是當你有兩個工作角色的實例運行時,在嘗試寫入Azure表時發生衝突。這很可能是因爲你的代碼看起來是這樣的:

var queueMessage = GetNextMessageFromQueue();  

Foo myFoo = GetFooFromTableStorage(queueMessage.FooId); 

if (myFoo == null) 
{ 
    myFoo = new Foo { 
         PartitionKey = queueMessage.FooId 
        }; 

    AddFooToTableStorage(myFoo); 
} 

DeleteMessageFromQueue(queueMessage); 

如果在隊列中具有相同FooId兩個相鄰的消息它很可能,你會與這兩個實例中的最終檢查Foo是否存在,但沒有找到它然後嘗試創建它。無論哪個實例是最後一次嘗試並保存該項目都會得到「實體已存在」錯誤。因爲它發生錯誤,所以它永遠不會到達代碼的刪除消息部分,因此它會在一段時間後重新回到隊列中。

正如其他人所說,處理毒訊息是一個非常好的主意。

更新27/02 如果不是後續消息(這基於你的分區/行密鑰方式我會說這是不可能的),那麼我的下一個賭注將是它的後在隊列中出現的背面,同樣的消息能見度超時。默認情況下,如果您使用.GetMessage(),則超時時間爲30秒。它有一個overload,它允許您指定該時間範圍的時間長度。還有.UpdateMessage() function可讓您在處理郵件時更新該超時。例如,您可以將初始可見性設置爲1分鐘,如果您在50秒後仍在處理該消息,請再延長一分鐘。

+0

這些消息是100%獨特的:)我更新了一些信息。 – 2012-02-24 07:59:28

1

對於需要考慮冪等性的隊列,期望並處理'EntityAlreadyExists'作爲可行的響應。

正如其他人建議的,原因可能是隊列中的具有相同標識符

  • 多個消息。
  • 正在偷看郵件,而不是從隊列中讀取它,因此不會使它們看不見。
  • 不刪除郵件,因爲在刪除郵件之前拋出異常。
  • 花太長時間處理消息,因此它不能被刪除(因爲隱形已超時),並再次

出現不看我猜測,這是無論是3或4個選項是代碼發生。

如果您無法通過代碼審查發現問題,可以考慮添加基於時間的日誌記錄和try/catch包裝以更好地理解。

在多角色環境中有效地使用隊列需要稍微不同的思維方式,早期遇到這樣的問題實際上是變相的祝福。

追加2/24

只是爲了澄清,修改不可見時間不出來是一個通用的解決方案,這種類型的問題。另請注意,儘管REST API中提供了此功能,但在隊列客戶端上可能不可用。

其他選項涉及以異步方式寫入表存儲以加快處理時間,但這又是一個停止間隔度量,它並沒有真正解決使用隊列的基本範式。

所以,底線是冪等的。您可以嘗試使用表存儲插件(更新或插入)功能,以避免出現'EntitiyAlreadyExists'錯誤,如果這適用於您的代碼。如果您所做的只是將新實體插入到azure表存儲中,那麼upsert應該用最少的代碼更改來解決您的問題。

如果你正在做更新,那麼它是一個不同的球類比賽。一種模式是將更新與同一表中具有相同分區鍵的僞插入進行配對,以便在更新之前發生錯誤並因此跳過更新。稍後消息被刪除後,您可以刪除虛擬插入。但是,所有這些都增加了複雜性,所以重新審視產品的架構會更好。例如,你是否真的需要插入/更新到如此多的表中?

+0

你的3和4選項'看起來'確實是對的。我發佈了更多信息。 – 2012-02-24 07:58:53

相關問題