storm定时器timer源码分析-timer.clj
storm定时器与java.util.Timer定时器比较相似。java.util.Timer定时器实际上是个线程,定时调度所拥有的TimerTasks;storm定时器也有一个线程负责调度所拥有的"定时任务"。storm定时器的"定时任务"是一个vector类型的数据[time, callback, uuid],内有会有三个值,分别是时间、函数、和uuid,很好理解,时间表示该定时任务什么时候执行,函数表示要执行的函数,uuid用于标识该"定时任务"。"定时任务"被存放到定时器的PriorityQueue队列中(和PriorityBlockingQueue区别,在于没有阻塞机制,不是线程安全的)。优先级队列是堆数据结构的典型应用,如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列(参阅 Comparable),也可以根据 Comparator 来指定,这取决于使用哪种构造方法。优先级队列不允许null元素。依靠自然排序的优先级队列还不允许插入不可比较的对象(这样做可能导致 ClassCastException)。当然也可以自己重新实现Comparator接口, 比如storm定时器就用reify重新实现了Comparator接口。storm定时器的执行过程比较简单,通过timer-thread,不断检查PriorityQueue里面时间最小的"定时任务"是否已经可以触发了, 如果可以(当前时间>=执行时间),就poll出来,调用callback,并sleep。storm定时器相关的函数均定义在timer.clj文件中,storm定时器是由mk-timer函数创建的,mk-timer函数定义如下:
mk-timer函数
))))
(catch Throwable t
;; Because the interrupted exception can be
;; wrapped in a RuntimeException.
;; 检查是否是InterruptedException,如果是InterruptedException,说明线程是由于接收interrupt信号而中断的,不做异常处理,否则调用kill-fn函数、修改线程状 ;; 态并抛出该异常
(when-not (exception-cause? InterruptedException t)
(kill-fn t)
(reset! active false)
(throw t)))))
;; release notifier信号量,标识timer—thread运行结束
(.release notifier)) thread-name)]
;; 设置timer-thread为守护线程
(.setDaemon timer-thread true)
;; 设置timer-thread为最高优先级
(.setPriority timer-thread Thread/MAX_PRIORITY)
;; 启动timer-thread线程
(.start timer-thread)
;; 返回该定时器的"属性"
{:timer-thread timer-thread
:queue queue
:active active
:lock lock
:cancel-notifier notifier}))
我们可以通过调用cancel-timer函数中断一个timer-thread线程,cancel-timer函数定义如下:
cancel-timer函数
[timer]
;; 检查timer状态是否是"active",如果不是则抛出异常
(check-active! timer)
;; 加锁
(locking (:lock timer)
;; 将timer的状态active设置成false,即"dead"
(reset! (:active timer) false)
;; 调用interrupt方法,中断线程,通过mk-timer函数我们可以知道在线程的run方法内调用了sleep方法,当接收到中断新号后会抛出InterruptedException异常使线程退出
(.interrupt (:timer-thread timer)))
;; acquire timer中的notifier信号量,因为只有当线程结束前才会release notifier信号量,所以此处是等待线程结束
(.acquire (:cancel-notifier timer)))
check-active!函数定义如下:
check-active!函数
[timer]
(when-not @(:active timer)
(throw (IllegalStateException. "Timer is not active"))))
通过调用schedule函数和schedule-recurring函数我们可以向storm定时器中添加"定时任务"。schedule函数定义如下:
schedule函数
;; timer绑定定时器,delay-secs绑定"定时任务"相对当前时间的延迟时间,afn绑定callback函数,check-active是否需要检查定时器
[timer delay-secs afn :check-active true]
;; 检查定时器状态
(when check-active (check-active! timer))
(let [id (uuid)
^PriorityQueue queue (:queue timer)]
;; 加锁,执行时间=当前时间+延迟时间,将"定时任务"的vector类型数据添加到PriorityQueue队列中
(locking (:lock timer)
(.add queue [(+ (current-time-millis) (secs-to-millis-long delay-secs)) afn id]))))
schedule-recurring函数定义如下:schedule-recurring函数也很简单,与schedule函数的区别就是在"定时任务"的callback函数中又添加了一个相同的"定时任务"。schedule函数的语义可以理解成向定时器添加
一个"一次性任务",schedule-recurring函数的语义可以理解成向定时器添加"一个周期执行的定时任务"(开始执行时间=当前时间+延迟时间,然后每隔recur-secs执行一次),
schedule-recurring函数
[timer delay-secs recur-secs afn]
(schedule timer
delay-secs
(fn this []
(afn)
; This avoids a race condition with cancel-timer.
(schedule timer recur-secs this :check-active false))))
nimbus检查心跳和重分配任务的实现就是通过schedule-recurring函数向storm定时器添加了一个"周期任务"实现的。
(conf NIMBUS-MONITOR-FREQ-SECS)
(fn []
(when (conf NIMBUS-REASSIGN)
(locking (:submit-lock nimbus)
(mk-assignments nimbus)))
(do-cleanup nimbus)
))
storm定时器timer源码分析-timer.clj的更多相关文章
- Nimbus<二>storm启动nimbus源码分析-nimbus.clj
nimbus是storm集群的"控制器",是storm集群的重要组成部分.我们可以通用执行bin/storm nimbus >/dev/null 2>&1 &a ...
- storm启动nimbus源码分析-nimbus.clj
nimbus是storm集群的"控制器",是storm集群的重要组成部分.我们可以通用执行bin/storm nimbus >/dev/null 2>&1 &a ...
- storm操作zookeeper源码分析-cluster.clj
storm操作zookeeper的主要函数都定义在命名空间backtype.storm.cluster中(即cluster.clj文件中).backtype.storm.cluster定义了两个重要p ...
- storm启动supervisor源码分析-supervisor.clj
supervisor是storm集群重要组成部分,supervisor主要负责管理各个"工作节点".supervisor与zookeeper进行通信,通过zookeeper的&qu ...
- storm shell命令源码分析-shell_submission.clj
当我们在shell里执行storm shell命令时会调用shell_submission.clj里的main函数.shell_submission.clj如下: shell_submission.c ...
- supervisor启动worker源码分析-worker.clj
supervisor通过调用sync-processes函数来启动worker,关于sync-processes函数的详细分析请参见"storm启动supervisor源码分析-superv ...
- Unity时钟定时器插件——Vision Timer源码分析之二
Unity时钟定时器插件——Vision Timer源码分析之二 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 前面的已经介绍了vp_T ...
- worker启动executor源码分析-executor.clj
在"supervisor启动worker源码分析-worker.clj"一文中,我们详细讲解了worker是如何初始化的.主要通过调用mk-worker函数实现的.在启动worke ...
- Java并发编程笔记之Timer源码分析
timer在JDK里面,是很早的一个API了.具有延时的,并具有周期性的任务,在newScheduledThreadPool出来之前我们一般会用Timer和TimerTask来做,但是Timer存在一 ...
随机推荐
- Entity Framework中的Identity map和Unit of Work模式(转)
一,什么是Identity map模式 Identity map是EF获取和缓存数据的模式.Identity map模式指的是任何数据都只会被加载一次,以map的形式缓存,以唯一的identity来再 ...
- 《python基础教程(第二版)》学习笔记 字符串(第3章)
<python基础教程(第二版)>学习笔记 字符串(第3章)所有的基本的序列操作(索引,分片,乘法,判断成员资格,求长度,求最大最小值)对字符串也适用.字符串是不可以改变的:格式化输出字符 ...
- EntityFramework 学习 一 Lazy Loading
延迟加载:延迟加载相关的数据 using (var ctx = new SchoolDBEntities()) { //Loading students only IList<Student&g ...
- APP被应用商店下架了怎么办?
上周五的时候,知乎被各大APP应用商店下架,原因是因为在应用内一些信息不符合相关的规定所以被强制性下架,看起来只是简单的一个应用被下架的事情.但是作为一个推广人员,我第一件事情想到的是,假如我自己公司 ...
- 在javascript中使用replace
javascript中使用replace功能很强大,不仅可以替换为字符串,还可以利用正则表达式,对匹配到的字符串为所欲为的进行操作,首先我们来了解一下replace的基本语法 定义和用法 replac ...
- 代码题(3)— 最小的k个数、数组中的第K个最大元素、前K个高频元素
1.题目:输入n个整数,找出其中最小的K个数. 例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4. 快排思路(掌握): class Solution { public ...
- PHP+MySQL百万级数据插入的优化
插入分析 MySQL中插入一个记录需要的时间由下列因素组成,其中的数字表示大约比例: 连接:(3) 发送查询给服务器:(2) 分析查询:(2) 插入记录:(1x记录大小) 插入索引:(1x索引) 关闭 ...
- css绝对对齐
方法1:使用text-align:justify 能够兼容所有的浏览器,但是一定要在模块和模块或者字之间存在空格,换行符或者制表符,这样才能起作用 ;;} /* 说明: 1.IE中要实现块内单行两端对 ...
- Mybatis异常_04_mapper.xml中不能使用小于号<、大于号>
一.解决方法: 1.< 2.<![CDATA[ < ]]> 将小于号替换为上述代码即可. 二.参考资料 1.mybatis配置中sql莫名其妙报错,多半是条件中小于符号捣的鬼
- 时间复杂度O(n)与空间复杂度O(1)
把输入规模看成x轴,所花时间/空间看成y轴.O(n)就是 y = x, y随x的增长而线性增长.一条斜线O(1)就是 y = 1,不管x如何变,y不变.一条与x平行的线 举个简单的例子,要从0加到n, ...