Trident state

原文地址  https://github.com/nathanmarz/storm/wiki/Trident-state 

Trident是一個很好的關於讀取或寫入有狀態資源的抽象。state可以是內部的topology(例如內存中和被HDFS支持的),或者是外部的存儲在如memcache或cassandra的數據庫中。這些情況在使用Trident API時,是沒有區別的。

Trident使用一種容錯機制來管理state,使得state的更新獨立於資源的失敗和重新發送。這也Trident topologies好像對每個消息只做一次處理的原因。

當對state進行更新的時候,可能會有很多層次的容錯。再接觸這些知識之前,我們先看一個例子來說明實現恰好一次的語義的技巧的必要性。假設你正在對一個流數據進行計數聚集,並且你將計數數據存儲在一個數據庫。現在假設你存一個數在數據庫,來表示計數數值。每次你處理一個新的tuple你對計數數值進行增加。

當失敗發生,tuple將會被重發。這帶來一個問題,當進行state更新時(或者相關的任何事情),你不知道是否在之前曾經成功更新過這個tuple。

也許你之前從未處理過這個tuple,這種情況下你可以增加計數。也許你已經處理過這個tuple並且也成功的增加了計數,但是這個tuple在其他步驟中處理失敗了。這種情況下,你不應該再增加計數了。或者也許你已經看到這個tuple但是在更新數據庫的時候得到一個錯誤,這時,你應該更新數據庫。

在存儲數據到數據庫中時,你不知道這個tuple是否之前已經被處理過了。所以你需要更多的信息來作出正確選擇。Trident提供了以下語義,他們是一組高效的實現一次處理的語義:

1 使用小的批處理來處理tuple。

2 每個tuple的批處理都有唯一的 "transaction id" (txid)。如果這個批處理重新執行,將會準確的給出一樣的txid。

3 State更新是通過有序的批處理完成的。也就是,批處理3對應的狀態更新一定會在批處理2更新成功之後再去執行。

基於這些原語,你的State實現可以檢查到tuple批處理是否已經執行過了,並且以一致的方式採取適當的行動更新state。你採取的方式依賴於每個批處理中的輸入spouts提供的準確原語。有三種可能的spouts支持容錯:無事物的,事務性的,非透明事物。同樣的,有3種可能的state支持容錯:無事物的,事務性的,非透明事物。讓我們來一下每種spout類型並且我們可以在相應的類型上實現什麼樣的容錯。

Transactional spouts

記住,Trident處理tuple是以一個個擁有唯一transaction id的小型批處理來實現的。不同屬性的spout爲batch提供不同的保證。事務型spout擁有以下屬性:

1 批處理的txid會一直保持相同。重新執行批處理的txid會與第一次執行該批處理時的txid完全相同。

2 tuple在批處理之間是不會有重疊或交集的。一個tuple只會在一個batch中。

3 所有tuple都在batch中。沒有一個tuples例外。

這是一個非常簡單易懂的spout類型。數據流被分成不會改變的批處理。trident-kafka 有一個事務型spout用於Kafka。

你可能想知道——爲什麼你不經常使用事務型spout?這個很容易理解。一個原因是,對於你的計算容錯不是必要的。比如,

the way TransactionalTridentKafkaSpout works is the batch for a txid will contain tuples from all the Kafka partitions for a topic. Once a batch has been emitted, any time that batch is re-emitted in the future the exact same set of tuples must be emitted to meet the semantics of transactional spouts. Now suppose a batch is emitted from TransactionalTridentKafkaSpout, the batch fails to process, and at the same time one of the Kafka nodes goes down. You're now incapable of replaying the same batch as you did before (since the node is down and some partitions for the topic are not unavailable), and processing will halt.

這就是爲什麼會有不透明事務spouts-它的容錯是對於丟失源數據節點仍然可以實現一次性執行的處理語義。你將會在後面的章節瞭解到它。

一個側面說明,如果kafka支持複製,那麼就需要使用支持節點容錯的事務型spout,但是這個功能目前還沒有。

在我們開始瞭解不透明事務型spout之前。我們先來看下如何設計一個擁有一次性語義的事務型spouts的state實現。state類型被稱作事務狀態 "transactional state",並且利用了任意給定的tuples相關的txid不會改變這一特性。

假設準備進行一個word count 計算,並且你希望將結果保存在一個 key/value的數據庫。KEY是word,value是語句中出現的數量。我們已經看到如果只保存count數將無法確定一個批處理是否完成。因此,我們應該存儲 將count和transaction id作爲一個原子數值存儲起來。當我們更新count的時候,比較數據庫中的txid和當前batch的txid。如果他們相同,根據強順序執行性,我們可以跳過這次更新。如果他們不同,則增加count的值。這個邏輯是可行的,因爲batch的txid是永遠不會改變的,Trident保證state的更新是完全按照batch的順序來完成的。

Consider this example of why it works. Suppose you are processing txid 3 which consists of the following batch of tuples:

考慮下這個例子是如何運行的。假設你正在處理txid3的事務,事務中batch的tuple爲

["man"]
["man"]
["dog"]

假設數據庫中有如下的key/value對:

man => [count=3, txid=1]
dog => [count=4, txid=3]
apple => [count=10, txid=2]

"man"相關的 txid 是 txid 1. 因爲當前的txid 爲3,你可以確定這個batch沒有被加入到這個count中。 所以你可以繼續執行將man的count數加上2並且更新 txid 爲3. 看另一個key/value對, "dog" 的txid是3,與當前的txid相同. 你可以確定這個batch已經被執行,所以你可以跳過此次更新。完成更新後,數據庫中的內容如下:

man => [count=5, txid=3]
dog => [count=4, txid=3]
apple => [count=10, txid=2]

Opaque transactional spouts

正如之前的描述,不透明事務spout不能保證tuple的batch txid一直保持不變。不透明事務spout有以下的屬性:

每個tuple都會被在一個batch中進行成功的處理。但是,這個tuple有可能是在一個batch處理失敗後,在之後的一個batch中處理成功。

OpaqueTridentKafkaSpout is a spout that has this property and is fault-tolerant to losing Kafka nodes. Whenever it's time for OpaqueTridentKafkaSpout to emit a batch, it emits tuples starting from where the last batch finished emitting. This ensures that no tuple is ever skipped or successfully processed by multiple batches.

使用不透明事務spou,在處理batch時不再會因爲當前事務txid與數據庫中的txid相同而跳過state的更新。因爲這個batch有可能在兩次state更新期間發生了改變。

What you can do is store more state in the database. Rather than store a value and transaction id in the database, you instead store a value, transaction id, and the previous value in the database. Let's again use the example of storing a count in the database. Suppose the partial count for your batch is "2" and it's time to apply a state update. Suppose the value in the database looks like this:

{ value = 4,
  prevValue = 1,
  txid = 2
}

Suppose your current txid is 3, different than what's in the database. In this case, you set "prevValue" equal to "value", increment "value" by your partial count, and update the txid. The new database value will look like this:

{ value = 6,
  prevValue = 4,
  txid = 3
}

Now suppose your current txid is 2, equal to what's in the database. Now you know that the "value" in the database contains an update from a previous batch for your current txid, but that batch may have been different so you have to ignore it. What you do in this case is increment "prevValue" by your partial count to compute the new "value". You then set the value in the database to this:

{ value = 3,
  prevValue = 1,
  txid = 2
}

This works because of the strong ordering of batches provided by Trident. Once Trident moves onto a new batch for state updates, it will never go back to a previous batch. And since opaque transactional spouts guarantee no overlap between batches – that each tuple is successfully processed by one batch – you can safely update based on the previous value.







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章