在上篇:Storm的基礎框架分析
基本探討了storm的:
但,感覺對nimbus、supervisor、並行度,任務分配和負載均衡的關系沒有交代清楚,而且細節上也有些瑕疵,本篇做一個補充。
這裡做一些補充:
supervisor會定時從zookeeper獲取topologies、已分配的任務分配信息assignments及各類心跳信息,以此為依據進行任務分配。
在supervisor周期性地進行同步時,會根據新的任務分配來啟動新的worker或者關閉舊的worker,以響應任務分配和負載均衡。
worker通過定期的更新connections信息,來獲知其應該通訊的其它worker。
worker啟動時,會根據其分配到的任務啟動一個或多個executor線程。這些線程僅會處理唯一的topology。
executor線程負責處理多個spouts或者多個bolts的邏輯,這些spouts或者bolts,也稱為tasks。
具體有多少個worker,多少個executor,每個executor負責多少個task,是由配置和指定的parallelism-hint共同決定的,但指定的並行度並不一定等於實際運行中的數目。
1、TOPOLOGY-WORKERS參數指定了某個topology運行時需啟動的worker數目.
2、parallelism-hint指定某個component(組件,如spout)的初始executor的數目.
3、TOPOLOGY-TASKS是component的tasks數,計算稍微復雜點:
(1). 如果未指定TOPOLOGY-TASKS,此值等於初始executors數.
(2). 如果已指定,和TOPOLOGY-MAX-TASK-PARALLELISM值進行比較,取小的那個作為實際的TOPOLOGY-TASKS.
用代碼來表達就是:
(defn- component-parallelism [storm-conf component]
(let [storm-conf (merge storm-conf (component-conf component))
num-tasks (or (storm-conf TOPOLOGY-TASKS) (num-start-executors component))
max-parallelism (storm-conf TOPOLOGY-MAX-TASK-PARALLELISM)
]
(if max-parallelism
(min max-parallelism num-tasks)
num-tasks)))
4、對於acker這種特殊的bolt來說,其並行度計算如下:
(1). 如果指定了TOPOLOGY-ACKER-EXECUTORS,按這個值計算.
(2). 如果未指定,那麼按TOPOLOGY-WORKERS的值來設置並行度,這種情況下,一個acker對應一個worker,顯然,在計算任務繁重、數據量比較大的情況下,這是不合適的。
5、如果配置了NIMBUS-SLOTS-PER-TOPOLOGY,在提交topology到nimbus時,會驗證topology所需的worker總數,如果超過了這個值,說明不能夠滿足需求,則拋出異常。
6、如果配置了NIMBUS-EXECUTORS-PER-TOPOLOGY,如第5點,會驗證topology所需的executor總數,如果超出,也會拋出異常。
同時,需要注意,實際運行中,有可能出現並行的TASKS數小於指定的數量。
通過調用nimbus接口的rebalance或者do-rebalance操作,以上並行度可被動態改變。
先回顧下任務分配中的幾個主要角色:
接著看幾段重要的並行度計算代碼:
1、計算所有topology的topology-id到executors的映射關系:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 計算所有tolopogy的topology-id到executors的映射
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- compute-topology->executors [nimbus storm-ids]
"compute a topology-id -> executors map"
(into {} (for [tid storm-ids]
{tid (set (compute-executors nimbus tid))})))
2、計算topology-id到executors的映射信息:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 計算topology-id到executors的映射
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- compute-executors [nimbus storm-id]
(let [conf (:conf nimbus)
storm-base (.storm-base (:storm-cluster-state nimbus) storm-id nil)
component->executors (:component->executors storm-base)
storm-conf (read-storm-conf conf storm-id)
topology (read-storm-topology conf storm-id)
task->component (storm-task-info topology storm-conf)]
(->> (storm-task-info topology storm-conf)
reverse-map
(map-val sort)
(join-maps component->executors)
(map-val (partial apply partition-fixed))
(mapcat second)
(map to-executor-id)
)))
3、計算topology的任務信息 task-info,這裡TOPOLOGY-TASKS就決定了每個組件component(spout、bolt)的並行度,或者說tasks數:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 計算topology的task-info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn storm-task-info
"Returns map from task -> component id"
[^StormTopology user-topology storm-conf]
(->> (system-topology! storm-conf user-topology)
all-components
;; 獲取每個組件的並行數
(map-val (comp #(get % TOPOLOGY-TASKS) component-conf))
(sort-by first)
(mapcat (fn [[c num-tasks]] (repeat num-tasks c)))
(map (fn [id comp] [id comp]) (iterate (comp int inc) (int 1)))
(into {})
))
4、上述1、2、3段代碼會在nimbus進行任務分配時調用,任務分配是通過mk-assignments函數來完成,調用過程用偽代碼描述如下:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; nimbus進行任務分配
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mk-assignments
;; 這一步計算topology的所有executor對應的node + port信息
->compute-new-topology->executor->node+port
->compute-topology->executors
-> ...
這裡回顧並補充下nimbus進行任務分配的主要流程:
1、nimbus將一組node + port 稱為worker-slot,由executor到worker-slot的映射信息,就決定executor將在哪台機器、哪個worker進程運行,隨之spout、bolt、acker等位置也就確定了,如下圖所示:
2、 nimbus是整個集群的控管核心,總體負責了topology的提交、運行狀態監控、負載均衡及任務分配等工作。
3、nimbus分配的任務包含了topology代碼所在的路徑(在nimbus本地)、tasks、executors及workers信息。
worker由node + port及配置的worker數量來唯一確定。
任務信息Assignment結構如下:
3、supervisor負責實際的同步worker的操作。一個supervisor稱為一個node。所謂同步worker,是指響應nimbus的任務分配,進行worker的建立、調度與銷毀。
在收到任務時,如果相關的topology代碼不在本地,supervisor會從nimbus下載代碼並寫入本地文件。
4、 通過node、port、host信息的計算,worker就知道和哪些機器進行通訊,而當負載均衡發生、任務被重新分配時,這些機器可能發生了變化,worker會通過周期性的調用refresh-connections來獲知變化,並進行新連接的建立、廢棄連接的銷毀等工作,如下圖所示:
supervisor、worker、executor等組件的心跳信息會同步至zookeeper,nimbus會周期性地獲取這些信息,結合已分配的任務信息assignments、集群現有的topologies(已運行+未運行)等等信息,來進行任務分配,如下圖所示:
1、通過rebalance和do-reblalance(比如來自web調用)觸發負載均衡,會觸發mk-assignments即任務分配。
2、同時,nimbus進程啟動後,會周期性地進行任務分配。
3、客戶端通過storm jar ... topology
方式提交topology,會通過thrift調用nimbus接口,提交topology,啟動新storm實例,並觸發任務分配。
負載均衡和任務分配是連在一起的,或者說任務分配中所用到的關鍵信息是由負載均衡來主導計算的,上文已經分析了任務分配的主要角色和流程,那麼負載均衡理解起來就很容易了,流程和框架如下圖所示:
其中,負載均衡部分的策略可采用平均分配、機器隔離或topology隔離後再分配、Round-Robin等等,因為主要討論storm的基礎框架,而具體的負載均衡策略各家都不一樣,而且這個策略是完全可以自定義的,比如可以將機器的實際能力如CPU、磁盤、內存、網絡等等資源抽象為一個一個的資源slot,以此slot為單位進行分配,等等。
這裡就不深入展開了。
通過負載均衡得出了新的任務分配信息assignments,nimbus再進行一些轉換計算,就會將信息同步到zookeeper上,supervisor就可以根據這些信息來同步worker了。
本篇作為對上篇的補充和完善.
也完整地回答了這個問題:
在Topology中我們可以指定spout、bolt的並行度,在提交Topology時Storm如何將spout、bolt自動發布到每個服務器並且控制服務的CPU、磁盤等資源的?
終。
Storm進程通信機制分析 http://www.linuxidc.com/Linux/2014-12/110158.htm
Apache Storm 的歷史及經驗教訓 http://www.linuxidc.com/Linux/2014-10/108544.htm
Apache Storm 的詳細介紹:請點這裡
Apache Storm 的下載地址:請點這裡