会发现, 现在storm里面有两套metrics系统, metrics framework和stats framework

并且在所有地方都是同时注册两套, 貌似准备用metrics来替代stats, 但当前版本UI仍然使用stats

这个模块统计的数据怎么被使用,

1. 在worker中, 会定期调用do-executor-heartbeats去往zk同步hb
可以看到, stats也会作为hb的一部分被同步到zk上

(defnk do-executor-heartbeats [worker :executors nil]
;; stats is how we know what executors are assigned to this worker
(let [stats (if-not executors
(into {} (map (fn [e] {e nil}) (:executors worker)))
(->> executors
(map (fn [e] {(executor/get-executor-id e) (executor/render-stats e)}))
(apply merge)))
zk-hb {:storm-id (:storm-id worker)
:

executor-stats stats

               :uptime ((:uptime worker))
:time-secs (current-time-secs)
}]
;; do the zookeeper heartbeat
(.worker-heartbeat! (:storm-cluster-state worker) (:storm-id worker) (:assignment-id worker) (:port worker) zk-hb)
))

2. 现在任何人都可以通过nimbus的thrift接口来得到相关信息

(^TopologyInfo getTopologyInfo [this ^String storm-id]
beats (.executor-beats storm-cluster-state storm-id (:executor->node+port assignment))
stats (:stats heartbeat))

3. 最直接的用户就是storm UI, 在准备topology page的时候, 就会调用getTopologyInfo来获取数据

(defn topology-page [id window include-sys?]
(with-nimbus nimbus
(let [summ (.getTopologyInfo ^Nimbus$Client nimbus id)]
)

Stats

这个模块用于spout和bolt来抽样统计数据, 需要统计的具体metics如下

(def COMMON-FIELDS [:emitted :transferred])
(defrecord CommonStats [emitted transferred rate]) (def BOLT-FIELDS [:acked :failed :process-latencies :executed :execute-latencies])
;;acked and failed count individual tuples
(defrecord BoltExecutorStats [common acked failed process-latencies executed execute-latencies]) (def SPOUT-FIELDS [:acked :failed :complete-latencies])
;;acked and failed count tuple completion
(defrecord SpoutExecutorStats [common acked failed complete-latencies])

抽样的比例在storm-conf, TOPOLOGY_STATS_SAMPLE_RATE, 配置

为什么统计时每次加rate, 而不是加1?

因为这里的统计是抽样的, 所以如果抽样比例是10%, 那么发现一个, 应该加1/(10%), 10个

(defn sampling-rate [conf]
(->> (conf TOPOLOGY-STATS-SAMPLE-RATE)
(/ 1)
int))

然后统计是基于时间窗口的, 底下是对应默认的bucket和时间窗口的定义

(def NUM-STAT-BUCKETS 20) ;;bucket数
;; 10 minutes, 3 hours, 1 day ;;定义3种时间窗口
(def STAT-BUCKETS [30 540 4320]) ;;bucket大小分别是30,540,4320秒

核心数据结构是RollingWindowSet, 包含:

统计数据需要的函数, updater extractor, 之所以治理也需要是因为需要统计all-time 
一组rolling windows, 默认是3个时间窗, 10 minutes, 3 hours, 1 day

all-time, 在完整的时间区间上的统计结果

(defrecord RollingWindowSet [updater extractor windows all-time])
(defn rolling-window-set [updater merger extractor num-buckets & bucket-sizes]
(RollingWindowSet. updater extractor (dofor [s bucket-sizes] (rolling-window updater merger extractor s num-buckets)) nil)
)

继续看看rolling window的定义,

核心数据, buckets, hashmap, {streamid, data}, 初始化为{}

统计data需要的函数, updater merger extractor

时间窗口, buckets大小和buckets个数

(defrecord RollingWindow [updater merger extractor bucket-size-secs num-buckets buckets])
(defn rolling-window [updater merger extractor bucket-size-secs num-buckets]
(RollingWindow. updater merger extractor bucket-size-secs num-buckets {}))

1. mk-stats

在mk-executedata的时候需要创建stats

mk-executor-stats <> (sampling-rate storm-conf)
;; TODO: refactor this to be part of an executor-specific map
(defmethod mk-executor-stats :spout [_ rate]
(stats/mk-spout-stats rate))
(defmethod mk-executor-stats :bolt [_ rate]
(stats/mk-bolt-stats rate))

第一个参数忽略, 其实就是分别调用stats/mk-spout-stats或stats/mk-bolt-stats, 可见就是对于每个需要统计的数据, 创建一个rolling-windows-set

(defn- mk-common-stats [rate]
(CommonStats. (atom (apply keyed-counter-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
(atom (apply keyed-counter-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
rate
)) (defn mk-bolt-stats [rate]
(BoltExecutorStats. (mk-common-stats rate)
(atom (apply keyed-counter-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
(atom (apply keyed-counter-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
(atom (apply keyed-avg-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
(atom (apply keyed-counter-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
(atom (apply keyed-avg-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
)) (defn mk-spout-stats [rate]
(SpoutExecutorStats. (mk-common-stats rate)
(atom (apply keyed-counter-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
(atom (apply keyed-counter-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
(atom (apply keyed-avg-rolling-window-set NUM-STAT-BUCKETS STAT-BUCKETS))
))

2. 数据更新

(defn spout-acked-tuple! [^SpoutExecutorStats stats stream latency-ms]
(update-executor-stat! stats :acked stream (stats-rate stats))
(update-executor-stat! stats :complete-latencies stream latency-ms)
)
(defmacro update-executor-stat! [stats path & args]
(let [path (collectify path)]
`(swap! (-> ~stats ~@path) update-rolling-window-set ~@args)
))

就以update-executor-stat! stats :acked stream (stats-rate stats)为例子看看怎么做的?

SpoutExecutorStats取出用于记录spout acked情况的rolling-windows-set
然后使用update-rolling-window-set来swap这个atom

来看看记录acked的rolling-windows-set是如何定义的?

keyed-counter-rolling-window-set, 预定义了updater merger extractor

updater, incr-val [amap key amt], 把给定的值amt加到amap的对应的key的value上

merger, (partial merge-with +), 用+作为map merge的逻辑, 即出现相同key则相加

extractor, counter-extract, (if v v {}), 有则返回, 无则返回{}

windows, rolling-window的list

all-time, 初始化为nil

(defn keyed-counter-rolling-window-set [num-buckets & bucket-sizes]
(apply rolling-window-set incr-val (partial merge-with +) counter-extract num-buckets bucket-sizes))

好, 下面就看看, 当spout-acked-tuple!时更新:acked时, 如何update的?

首先更新每个rolling-window, 并把更新过的rolling-window-set更新到:windows
并且更新:all-time, (apply (:updater rws) (:all-time rws) args)

updated, incr-val [amap key amt]

args, steamid, rate

all-time, 是用来记录整个时间区间上的, 某个stream的统计情况

(defn update-rolling-window-set
([^RollingWindowSet rws & args]
(let [now (current-time-secs)
new-windows (dofor [w (:windows rws)]
(apply update-rolling-window w now args))]
(assoc rws :windows new-windows :all-time (apply (:updater rws) (:all-time rws) args))
)))

看下如何更新某个rolling-windw
根据now算出当前属于哪个bucket, time-bucket

取出buckets, 并使用:updater更新相应的bucket, 这里的操作仍然是把rate叠加到streamid的value上

(defn update-rolling-window
([^RollingWindow rw time-secs & args]
;; this is 2.5x faster than using update-in...
(let [time-bucket (curr-time-bucket time-secs (:bucket-size-secs rw))
buckets (:buckets rw)
curr (get buckets time-bucket)
curr (apply (:updater rw) curr args)
]
(assoc rw :buckets (assoc buckets time-bucket curr))
)))

Storm-源码分析-Stats (backtype.storm.stats)的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. Nimbus<三>Storm源码分析--Nimbus启动过程

    Nimbus server, 首先从启动命令开始, 同样是使用storm命令"storm nimbus”来启动看下源码, 此处和上面client不同, jvmtype="-serv ...

  8. storm源码分析之topology提交过程

    storm集群上运行的是一个个topology,一个topology是spouts和bolts组成的图.当我们开发完topology程序后将其打成jar包,然后在shell中执行storm jar x ...

  9. JStorm与Storm源码分析(五)--SpoutOutputCollector与代理模式

    本文主要是解析SpoutOutputCollector源码,顺便分析该类中所涉及的设计模式–代理模式. 首先介绍一下Spout输出收集器接口–ISpoutOutputCollector,该接口主要声明 ...

  10. Storm-源码分析- hook (backtype.storm.hooks)

    task hook 在某些task事件发生时, 如果用户希望执行一些额外的逻辑, 就需要使用hook 当前定义如下事件, emit, cleanup, spoutAck-- 用户只需要开发实现ITas ...

随机推荐

  1. 图解Sysprep封装系统

    图解Sysprep封装系统     一.使用安装管理器工具创建 Sysprep.inf 应答文件 要安装“安装管理器”工具并创建应答文件,请按照下列步骤操作: 1)打开“我的电脑”,然后打开 Wind ...

  2. UIApplication深入学习

    多时候,我们不需要关心这个类,我们很少继承这个类,偶尔会调用这个类的api来实现一些功能,但是不可否认,这个类是iOS编程中很重要的一个概念.UIApplication的核心作用是提供了iOS程序运行 ...

  3. libcpmt.lib (xxx.obj) LNK2038: mismatch detected for 'RuntimeLibrary': value 'MT_StaticRelease' doesn't match value 'MD_DynamicRelease' in XXX.obj

    问题描述: 这样的,我写了个NString类,然后用的VS2013的命令行编译的(NMAKE.exe),并用LNK.exe打包成了NString.lib 然后后来我在VS2013里面建了一个proje ...

  4. scala flatMap reduceLeft foldLeft

    object collection_t1 { def flatMap1(): Unit = { val li = List(,,) val res = li.flatMap(x => x mat ...

  5. Ubuntu 12.04下LAMP环境的搭建

    首先 apt-get install update 一下 sudo apt-get install update 安装MySQL 的服务端和客户端 sudo apt-get install mysql ...

  6. [dts]TI-am437x dts

    imx6 可以参考http://blog.csdn.net/shengzhadon/article/details/49908439 参照文件: Documentation/devicetree/bi ...

  7. su和sudo命令

    su命令用于在不同的用户之间切换,比如使用user1登陆了系统,但要执行一些管理操作,比如useradd,普通用户没有这个权限的,解决的办法有两个. 1:退出user1用户,重新以root用户登录系统 ...

  8. getopt--parse command line options

    getopt解析命令行选项 getopt, getopt_long, getopt_long_only, optarg, optind, opterr, optopt - Parse command- ...

  9. Struts框架可以支持以下哪种程序开发语言?(选择1项)

    A.C B.C++ C.Java D.C# 解答:C

  10. leetcode:Pascal&#39;s Triangle

    一.     题目 经典题目,杨辉三角,输入行数.生成杨辉三角的数组. 二.     分析 首先,我们知道有例如以下规律: 1.每一行的第一个数和最后一个数都为1 2.中间的数是上面数和上面数左边的数 ...