目錄
框架介紹
storm是一個分佈式,高容錯的實時計算系統,對數據實時計算提供了簡單的spout和bolt原語。
Storm應用場景總結:
數據流處理: 與其它流處理系統不同,storm不需要中間隊列媒介
實時計算: 可連續不斷的進行實時數據處理,把處理的結果實時更新展示到客戶端
分佈式遠程過程調用: 可充分利用集羣中CPU資源,進行CPU密集型計算。
Storm體系結構
-
主控節點和工作節點
Storm 將每個節點分爲主控節點和工作節點兩種,其中主控節點只有一個,工作節點可以有很多個。
-
Nimbus:
負責資源分配和任務調度。主控節點運行 Nimbus 守護進程,負責在集羣中分發代碼,對節點分配任務,並監視主機故障。
-
Zookeeper:
負責Nimbus和多個Supervisor之間的所有協調工作。
-
Supervisor:
負責接受nimbus分配的任務,啓動和停止屬於自己管理的worker進程。 每個工作節點運行 Supervisor 守護進程,負責監聽工作節點上已經分配的主機作業,啓動和停止 Nimbus 已經分配的工作進程。supervisor 會定時從 zookeeper 獲取拓撲信息 topologies、任務分配信息 assignments 及各類心跳信息,以此爲依據進行任務分配。在 supervisor 同步時,會根據新的任務分配情況來啓動新的 worker 或者關閉舊的 worker 並進行負載均衡。
-
Worker:
工作進程,一個工作進程中可以含有一個或者多個Executor線程。
-
Executor:
線程,裏面運行着多個Task。
-
Task:
worker中每一個spout/bolt的線程稱爲一個task. 一個task中一定是運行的是相同組件。
Storm組件
Topology: 用於封裝一個實時計算應用程序的邏輯,類似於Hadoop的MapReduce Job;
Stream: 消息流,是一個沒有邊界的tuple序列,這些tuples會被以一種分佈式的方式並行地創建和處理;
Spouts: 數據源,是消息生產者,他會從一個外部源讀取數據並向topology裏面面發出消息:tuple;
Bolts: 處理消息,所有消息處理邏輯被封裝在bolts裏面,處理輸入的數據流併產生新的輸出數據流,可執行過濾,聚合,查詢數據庫等操作;
Stream groupings: 消息分發策略,定義一個Topology的其中一步是定義每個tuple接受什麼樣的流作爲輸入,stream grouping就是用來定義一個stream應該如何分配給Bolts們。
關係簡介
-
Worker進程存在於每個工作節點Supervisor中,一個Worker進程中可以含有一個或者多個Executor線程,每個Executor線程都會啓動一個消息循環線程,用於接收、處理和發送消息,當Executor收到其下某一task的消息後,就會調用該Task對應的處理邏輯對消息進行處理;
-
1個topology可以有多個worker進程,1個worker進程只爲1個topology服務。即1個worker進程執行的是1個topology的子集;
-
一個線程Executor,運行時只會運行一個task,如果有多個,循環執行,即其他的task出去等待狀態;
-
task,最終運行spout或bolt中代碼的執行單元,即task可能是spout組件也有可能是bolt組件;
-
默認情況下: 1個supervisor節點最多可以啓動4個worker進程,每1個topology默認佔用1個worker進程,每個spout或者bolt會佔用1個executor,每個executor啓動1個task。
-
注意 : 在同一個線程中,如果有多個task,這些task一定是相同組件實例;
舉例說明:
假設現在有一個Topology實例,在該Topology實例中,配置爲整個Topology實例服務的進程數量爲10,配置了Spout單元和Bolt單元以及Spout單元和Bolt單元之間的數據流,爲這些Spout和Bolt單元服務的線程和任務數量分別爲:Spout單元,10個線程,20個任務;Bolt單元,20個線程,20個任務。
那麼一旦將該Topology實例提交給Nimbus,接下來就會由Nimbus控制運行。
在Nimbus的控制下,有些Supervisor會在所在的worker node上建立一個進程,整個Supervisor集羣中共建立10個進程,這些進程都爲該Topology實例服務。這些進程可以運行在多個worker node上,也可以運行在同一臺worker node上。每個進程都持有對項目JAR包的引用。
現在一共需要30個線程來爲Spout和Bolt單元服務,那麼10個進程中,每個進程上運行3個線程。一個進程中的3個線程可以分別爲不同的Spout單元和Bolt單元服務。每個線程都創建一份Spout單元或者Bolt單元的實例。
Spout單元共有10個線程,20個任務爲其服務,那麼每個線程上運行2個任務,同理爲Bolt單元服務的20個線程中的每個線程上運行1個任務。
每個線程中的任務使用線程所持有的Spout實例或者Bolt實例,同一個線程中的多個任務間是串行執行的關係,因而在一個線程有多個任務的情況下,不會產生併發問題。
比如某個線程中持有一個Spout實例spoutInstance,配置該線程中需要運行5個任務,那麼Storm的框架代碼有可能是這麼實現的:
for(int i=0;i<5;i++) {
spoutInstance.nextTuple();
}
故障容忍
概念
-
Worker進程不會因Nimbus或者Supervisor的掛掉而受到影響
-
worker進程死掉: Supervisor會重啓它。如果這個Worker連續在啓動時失敗,並且無法讓Nimbus觀察到它的心跳,Nimbus將這個Worker重新分配到另一臺機器上。
-
supervisor進程死掉: 這樣不會影響之前已經提交的topology的運行,只是後期不會再向這個節點分配任務了。
-
nimbus進程死掉: 這樣不會影響之前已經提交的topology的運行,只是後期不能向集羣中提交topology了。
-
-
Nimbus和Supervisor daemon進程,設計成快速失敗(無論何時當遇到任何異常情況,將會執行自毀)和無狀態(所有的狀態都保存在Zookeeper或者磁盤上)。
-
Nimbus和Supervisor daemon進程,必須在監控下運行,如使用daemontools或者monit工具。
-
Nimbus是會有單點故障的問題,但Nimbus進程掛掉也不會引起任何災難發生。
三種消息保證機制
Storm提供的三種不同消息保證機制中,利用Spout,Bolt以及Acker的組合可以實現At Most Once以及At Least Once語義,Storm在At Least Once的基礎上進行了一次封裝(Trident),從而實現Exactly Once語義
At Most Once語義
Storm的消息保證機制中,如果需要實現At Most Once語義,只需要滿足下面任何一條即可:
1.關閉ACK機制,即Acker數目設置爲0
2.Spout不實現可靠性傳輸:Spout發送消息是使用不帶message ID的API。不實現fail函數。
3.Bolt不把處理成功或失敗的消息發送給Acker
At Least Once語義
如果需要實現At Least Once 語義,則需要同時保證如下幾條:
1.開啓ACK機制,即Acker數目大於0
2.Spout實現可靠性傳輸保證:Spout發送消息附帶message 的ID。如果收到Acker的處理失敗反饋,需要進行消息重傳,即實現fail函數。
3.Bolt在處理成功或失敗後需要調用相應的方法通知Acker
At least once 的消息處理機制,在運用時需要格外小心,Storm 採用 ack/fail 機制來追蹤消息的流向,當一個消息(tuple)發送到下游時,如果超時未通知 spout,或者發送失敗,Storm 默認會根據配置策略進行重發,可通過調節重發策略來儘量減少消息的重複發送。一個常見情況是,Storm 集羣經常會超負載運行,導致下游的 bolt 未能及時 ack,從而導致 spout 不斷的重發一個 tuple,進而導致消息大量的重複消費。
-
不發生任何異常的情況下,消息不會重複不會丟失。
-
Spout 發生異常的情況下,消息的重複數目約等於 spout.max.pending(Spout 的配置項,每次可以發送的最多消息條數) * NumberOfException(異常次數)。
-
Acker 發生異常的情況下,消息重複的數目等於 spout.max.pending * NumberOfException。
-
Bolt 發生異常的情況:
1.emit 之前發生異常,消息不會重複。
2.emit 之後發生異常,消息重複的次數等於異常的次數。
Exactly Once語義
實現Exactly Once語義,則需要在At Least Once的基礎上進行狀態的存儲,用來防止重複發送的數據被重複處理,在Storm中使用Trident API實現
數據流分組
定義數據流數據流應該發送到那些bolt中。數據流分組就是將數據流進行分組,按需要進入不同的bolt中。可以使用Storm提供的分組規則,也可以實現backtype.storm.grouping.CustomStreamGrouping自定義分組規則。Storm定義了8種內置的數據流分組方法:
-
Shuffle grouping(隨機分組):隨機分發tuple給bolt的各個task,每個bolt實例接收到相同數量的tuple;
-
Fields grouping(按字段分組):根據指定字段的值進行分組。比如,一個數據流按照"user-id"分組,所有具有相同"user-id"的tuple將被路由到同一bolt的task中,不同"user-id"可能路由到不同bolt的task中;
-
Partial Key grouping(部分key分組):數據流根據field進行分組,類似於按字段分組,但是將在兩個下游bolt之間進行均衡負載,當資源發生傾斜的時候能夠更有效率的使用資源。The Power of Both Choices: Practical Load Balancing for Distributed Stream Processing Engines提供了更加詳細的說明;
-
All grouping(全複製分組):將所有tuple複製後分發給所有bolt的task。小心使用。
-
Global grouping(全局分組):將所有的tuple路由到唯一一個task上。Storm按照最小的task ID來選取接收數據的task;(注意,當時用全局分組是,設置bolt的task併發是沒有意義的,因爲所有tuple都轉發到一個task上。同時需要注意的是,所有tuple轉發到一個jvm實例上,可能會引起storm集羣某個jvm或服務器出現性能瓶頸或崩潰)
-
None grouping(不分組):這種分組方式指明不需要關心分組方式。實際上,不分組功能與隨機分組相同。預留功能。
-
Direct grouping(指向型分組):數據源會調用emitDirect來判斷一個tuple應該由哪個storm組件接收,只能在聲明瞭指向型的數據流上使用。
-
Local or shuffle grouping(本地或隨機分組):當同一個worker進程中有目標bolt,將把數據發送到這些bolt中。否則,功能將與隨機分組相同。該方法取決與topology的併發度,本地或隨機分組可以減少網絡傳輸,降低IO,提高topology性能。
參考文章
Storm中-Worker Executor Task的關係
Related Posts: