在backtype.storm.cluster.clj中, 定义了storm对于Zookeeper的使用

 

ClusterState

首先定义操作Zookeeper集群的interface

(defprotocol ClusterState
(set-ephemeral-node [this path data])
(delete-node [this path])
(create-sequential [this path data])
(set-data [this path data]) ;; if node does not exist, create persistent with this data
(get-data [this path watch?])
(get-children [this path watch?])
(mkdirs [this path])
(close [this])
(register [this callback])
(unregister [this id])
)

实现和生成用于操作Zookeeper集群的record

首先创建zk-client, 并在zk上创建STORM-ZOOKEEPER-ROOT目录

接着定义,

    callbacks, callback集合

    active, 标志zk集群状态

    zk, zk client

创建zk client的时候, 设置了watcher, 即zk server当状态发生变化时会给client发送event, 此处client设置的watcher会调用callbacks来处理server发送的event

Storm在操作Zookeeper时, 使用CuratorFramework(http://curator.incubator.apache.org/curator-framework/index.html)

最后实现ClusterState protocol, 其中register和unregister是用来添加/删除callbacks的, 其他都是些zk的常规操作

(defn mk-distributed-cluster-state [conf]
(let [zk (zk/mk-client conf (conf STORM-ZOOKEEPER-SERVERS) (conf STORM-ZOOKEEPER-PORT) :auth-conf conf)]
(zk/mkdirs zk (conf STORM-ZOOKEEPER-ROOT))
(.close zk))
(let [callbacks (atom {})
active (atom true)
zk (zk/mk-client conf
(conf STORM-ZOOKEEPER-SERVERS)
(conf STORM-ZOOKEEPER-PORT)
:auth-conf conf
:root (conf STORM-ZOOKEEPER-ROOT)
:watcher (fn [state type path]
(when @active
(when-not (= :connected state)
(log-warn "Received event " state ":" type ":" path " with disconnected Zookeeper."))
(when-not (= :none type)
(doseq [callback (vals @callbacks)]
(callback type path))))
))]
(reify
ClusterState
(register [this callback]
(let [id (uuid)]
(swap! callbacks assoc id callback)
id
))
(unregister [this id]
(swap! callbacks dissoc id)) (set-ephemeral-node [this path data]
(zk/mkdirs zk (parent-path path))
(if (zk/exists zk path false)
(try-cause
(zk/set-data zk path data) ; should verify that it's ephemeral
(catch KeeperException$NoNodeException e
(log-warn-error e "Ephemeral node disappeared between checking for existing and setting data")
(zk/create-node zk path data :ephemeral)
))
(zk/create-node zk path data :ephemeral)
)) (create-sequential [this path data]
(zk/create-node zk path data :sequential)) (set-data [this path data]
;; note: this does not turn off any existing watches
(if (zk/exists zk path false)
(zk/set-data zk path data)
(do
(zk/mkdirs zk (parent-path path))
(zk/create-node zk path data :persistent)
))) (delete-node [this path]
(zk/delete-recursive zk path)
) (get-data [this path watch?]
(zk/get-data zk path watch?)
) (get-children [this path watch?]
(zk/get-children zk path watch?)) (mkdirs [this path]
(zk/mkdirs zk path)) (close [this]
(reset! active false)
(.close zk))
)))

 

StormClusterState

定义针对Storm定制的zk操作协议, 包含各种storm里面的信息在zk上的读写

(defprotocol StormClusterState
(assignments [this callback])
(assignment-info [this storm-id callback])
(active-storms [this])
(storm-base [this storm-id callback]) (get-worker-heartbeat [this storm-id node port])
(executor-beats [this storm-id executor->node+port])
(supervisors [this callback])
(supervisor-info [this supervisor-id]) ;; returns nil if doesn't exist (setup-heartbeats! [this storm-id])
(teardown-heartbeats! [this storm-id])
(teardown-topology-errors! [this storm-id])
(heartbeat-storms [this])
(error-topologies [this]) (worker-heartbeat! [this storm-id node port info])
(remove-worker-heartbeat! [this storm-id node port])
(supervisor-heartbeat! [this supervisor-id info])
(activate-storm! [this storm-id storm-base])
(update-storm! [this storm-id new-elems])
(remove-storm-base! [this storm-id])
(set-assignment! [this storm-id info])
(remove-storm! [this storm-id])
(report-error [this storm-id task-id error])
(errors [this storm-id task-id]) (disconnect [this])
)

首先判断是否第一次mk-storm-cluster-state, 既是否进行过zk cluster state的创建, 如果没有调用mk-distributed-cluster-state

接着, 定义一系列的callbacks, 并调用cluster-state的register, 注册到callbacks列表中

       state-id 就是register返回的callback的uuid

再者, 在zk上创建storm的子目录

最后, 实现StormClusterState协议, 实现各种zk数据的读写

(defn mk-storm-cluster-state [cluster-state-spec]
(let [[solo? cluster-state] (if (satisfies? ClusterState cluster-state-spec)
[false cluster-state-spec]
[true (mk-distributed-cluster-state cluster-state-spec)])
assignment-info-callback (atom {})
supervisors-callback (atom nil)
assignments-callback (atom nil) ;在StormClusterState.assignments中被set
storm-base-callback (atom {})
state-id (register
cluster-state
(fn [type path]
(let [[subtree & args] (tokenize-path path)] ;将path按'/'分割
(condp = subtree ;对path的subtree部分进行swith…case
ASSIGNMENTS-ROOT (if (empty? args)
(issue-callback! assignments-callback) ;issue-callback!, 执行并删除该callback, 保证callback只被执行一次
(issue-map-callback! assignment-info-callback (first args)))
SUPERVISORS-ROOT (issue-callback! supervisors-callback)
STORMS-ROOT (issue-map-callback! storm-base-callback (first args))
;; this should never happen
(halt-process! 30 "Unknown callback for subtree " subtree args)
)
)))]
(doseq [p [ASSIGNMENTS-SUBTREE STORMS-SUBTREE SUPERVISORS-SUBTREE WORKERBEATS-SUBTREE ERRORS-SUBTREE]]
(mkdirs cluster-state p))
(reify
StormClusterState
)

 

例子

通过一个场景来说明storm怎样使用zookeeper

supervisor中的mk-synchronize-supervisor, 主要用于下载新的, 并删除不使用的topology代码

所以这个逻辑光执行一次是不够的, 需要当每次assignment发生变化的时候就执行一次

storm是利用zookeeper的watcher来解决这个问题

1. 在mk-distributed-cluster-state中创建zk client的时候配置watcher, 当收到zk server的event的时候, 调用callbacks列表里面的callback进行处理

2. 在mk-storm-cluster-state 中将callback加入cluster-state的callback列表

    而这个callback本身, 就是根据event中的path(代表哪部分数据发生change)来issue在storm-cluster-state中维护的一系列callback

    比如, 当ASSIGNMENTS-ROOT发生变化时, 会调用assignments-callback 
3. 那么也就是说只需要将mk-synchronize-supervisor, set到assignments-callback, 就可以保证当ASSIGNMENTS-ROOT发生变化时, 调用mk-synchronize-supervisor去同步topology代码

   什么时候set? 在第一次调用mk-synchronize-supervisor的时候

sync-callback (fn [& ignored] (.add event-manager this))
assignments-snapshot (assignments-snapshot storm-cluster-state sync-callback)

   同步topology代码是消耗时间的事情, 所以实现的时候放在后台执行, 只是将this(function) add到event-manager的queue里面, 后台线程会执行这个函数

   并且在调用assignment获取assignments-snapshot的时候, 将sync-callback set到assignments-callback中去

     (assignments [this callback]
(when callback
(reset! assignments-callback callback))
(get-children cluster-state ASSIGNMENTS-SUBTREE (not-nil? callback)))

By the way, 对于get-children, 是否有callback, 即是否被watch, 读的数据是不一样的, 具体原因不是很清楚, 需要后面看看zk的具体使用

(defn get-children [^CuratorFramework zk ^String path watch?]
(if watch?
(.. zk (getChildren) (watched) (forPath (normalize-path path)))
(.. zk (getChildren) (forPath (normalize-path path)))))

4. 前面说了issue-callback!在执行assignments-callback之前, 会将其清空, 所以如果需要不断的触发, 那么就要不断的设置assignments-callback

    所以作为callback, mk-synchronize-supervisor会先通过assignments-snapshot去重设assignments-callback

    至于为什么要采用这样的机制? 现在还看不清楚

Storm-源码分析- Storm中Zookeeper的使用的更多相关文章

  1. Storm源码分析--Nimbus-data

    nimbus-datastorm-core/backtype/storm/nimbus.clj (defn nimbus-data [conf inimbus] (let [forced-schedu ...

  2. JStorm与Storm源码分析(一)--nimbus-data

    Nimbus里定义了一些共享数据结构,比如nimbus-data. nimbus-data结构里定义了很多公用的数据,请看下面代码: (defn nimbus-data [conf inimbus] ...

  3. JStorm与Storm源码分析(二)--任务分配,assignment

    mk-assignments主要功能就是产生Executor与节点+端口的对应关系,将Executor分配到某个节点的某个端口上,以及进行相应的调度处理.代码注释如下: ;;参数nimbus为nimb ...

  4. storm源码分析之任务分配--task assignment

    在"storm源码分析之topology提交过程"一文最后,submitTopologyWithOpts函数调用了mk-assignments函数.该函数的主要功能就是进行topo ...

  5. JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler

    EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口, 由下面代码可以看出: (ns backtype.storm.scheduler.EvenSche ...

  6. JStorm与Storm源码分析(三)--Scheduler,调度器

    Scheduler作为Storm的调度器,负责为Topology分配可用资源. Storm提供了IScheduler接口,用户可以通过实现该接口来自定义Scheduler. 其定义如下: public ...

  7. storm源码之storm代码结构【译】【转】

    [原]storm源码之storm代码结构[译]  说明:本文翻译自Storm在GitHub上的官方Wiki中提供的Storm代码结构描述一节Structure of the codebase,希望对正 ...

  8. storm源码之storm代码结构【译】

    storm源码之storm代码结构[译] 说明:本文翻译自Storm在GitHub上的官方Wiki中提供的Storm代码结构描述一节Structure of the codebase,希望对正在基于S ...

  9. 【原】storm源码之storm代码结构【译】

    说明:本文翻译自Storm在GitHub上的官方Wiki中提供的Storm代码结构描述一节Structure of the codebase,希望对正在基于Storm进行源码级学习和研究的朋友有所帮助 ...

  10. angular源码分析:angular中脏活累活承担者之$parse

    我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...

随机推荐

  1. sublime插件@sublimelinter安装使用

    sublimelinter插件是一款sublime编辑器的代码校验插件,支持多种语言,对于前端来说主要包含css和js校验. 要是用这款插件 1)安装node,然后在全局安装jshint(npm in ...

  2. 問題排查:DataGridView 資料行下拉選單,資料繫結階段顯示 DataGridViewComboBoxCell 值無效

    可能原因: 1.下拉選單的選項資料繫結晚於 DataGridView 的資料繫結 2.下拉選單的 DataPropertyName 屬性,比 DisplayMember.ValueMember 早賦值 ...

  3. solaris安装oracle遇INS 30131 错误

    安装11.2.0.4,报错: [FATAL] [INS-30131] Initial setup required for the execution of installer validations ...

  4. 移动开发tip

    input点击出现背景色和边框,加入样式 -webkit-tap-highlight-color: rgba(255,255,255,0); ios下按钮糊掉,样式表不怎么起作用,使用-webkit- ...

  5. iOSDate时间格式(转)

    在开发iOS程序时,有时候需要将时间格式调整成自己希望的格式,这个时候我们可以用NSDateFormatter类来处理.例如: //实例化一个NSDateFormatter对象 NSDateForma ...

  6. 理解soft-clipped reads

    什么是soft-clipped reads 当基因组发生某一段的缺失,或转录组的剪接,在测序过程中,横跨缺失位点及剪接位点的reads回帖到基因组时,一条reads被切成两段,匹配到不同的区域,这样的 ...

  7. ADV-时间分配

    #include<stdio.h> int map[20][4]; typedef struct node{ int star; int end; }node; node dui[100] ...

  8. 协程,事件驱动,异步io模型,异步网络框架

    协程是一种用户态的轻量级线程,内核不知道它的存在.协程运行于一个线程中,协程的切换是由用户控制的.线程的切换是由cpu来控制的,而协程的切换是由用户控制的.协程的执行时串行的. select/poll ...

  9. mac端的优秀抓包工具——Charles使用

    http://my.oschina.net/u/2340880/blog/508688 mac端的优秀抓包工具——Charles使用 一.简介 二.安装与使用 三.使用Charles在mac上进行抓包 ...

  10. 【Java】:googleSearch

    google custom search是一个基于google的搜索引擎api,可以请求谷歌的搜索数据 pala pala  pala  ... 实现: 1.注册谷歌账号 2.创建google项目 1 ...