一、簡介:
storm中有一個很重要的特性:
保證發出的每個tuple都會被完整處理。一個tuple被完全處理的意思是: 這個tuple以及由這個tuple所產生的所有的子tuple都被成功處理。
如果任一個消息在timeout所指定的時間內沒有完成處理,那這個tuple就失敗了。
二、原理:
acker並不會為每個tuple都分配內存空間來完成跟蹤,而是利用了一個非常巧妙的算法,這個算法只需使用恆定的20字節就可以完成整個tuple樹的跟蹤。
具體原理:
acker對於每個spout-tuple保存一個ack-val的校驗值,它的初始值是0, 然后每發射一個tuple/ack一個tuple,那么tuple的id都要跟這個校驗值異或一下,
並且把得到的值更新為ack-val的新值。那么假設每個發射出去的tuple都被ack了, 那么最后ack-val一定是0(因為一個數字跟自己異或得到的值是0)。
通俗理解:
1.在spout產生一條tuple時,會向acker發送一條信息,讓ack來進行跟蹤,消息內容:
spout-tuple-id:這條tuple的id,每條tuple都會產生一個隨機的MessageId
task-id:產生這條tuple的id,spout可能有多個task,每個task都會被分配一個唯一的taskId
ack-val:默認值為0,用來跟蹤tuple
2.acker會在自己的map(類型為TimeCacheMap)里保存這條記錄。 這就是acker對spout-tuple進行跟蹤的核心數據結構, 對於每個spout-tuple所產生的tuple樹的跟蹤
都只需要保存上面這條記錄。acker后面會檢查:val什么時候變成0,變成0, 說明這個spout-tuple產生的tuple都處理完成了。
3.spout在發送完消息給acker后會將該tuple和MessageId發送到boltTask。boltTask在創建子tuple時並不會向acker發送消息讓其跟蹤,而是很巧妙的省略了這一步:
bolt在發射一個新的bolt的時候會把這個新tuple跟它的父tuple的關系保存起來(strom稱之為anchoring)。然后在ack tuple的時候,storm會把要ack的tuple的id, 以及
這個tuple新創建的所有的tuple的id的異或值發送給acker。消息格式是:(spout-tuple-id,tmp-ack-val)執行完這一步后,ack-val的值就變成了所有子tuple的id的異或值
ps:storm使用一致性哈希來把一個spout-tuple-id對應到acker, 因為每一個tuple知道它所有的祖宗的tuple-id, 所以它自然可以算出要通知哪個acker來ack
4.當所有子tuple都被ack之后,val會被異或成0,OK 整個tuple樹執行跟蹤完成。
場景分析:
1. 由於對應的task掛掉了,一個tuple沒有被ack: storm的超時機制在超時之后會把這個tuple標記為失敗,從而可以重新處理。
2. Acker掛掉了: 這種情況下由這個acker所跟蹤的所有spout tuple都會超時,也就會被重新處理。
3. Spout掛掉了: 在這種情況下給spout發送消息的消息源負責重新發送這些消息。比如Kestrel和RabbitMQ在一個客戶端斷開之后會把所有”處理中“的消息放回隊列。
由此可見storm的高度容錯性。
原文鏈接:http://blog.itpub.net/29754888/viewspace-1261363/