EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口, 
由下面代码可以看出:

(ns backtype.storm.scheduler.EvenScheduler
(:use [backtype.storm util log config])
(:require [clojure.set :as set])
(:import [backtype.storm.scheduler IScheduler Topologies
Cluster TopologyDetails WorkerSlot ExecutorDetails])
(:gen-class
:implements [backtype.storm.scheduler.IScheduler])) EvenScheduler是一个对资源进行均匀分配的调度器:
(defn -prepare [this conf]
) (defn -schedule [this ^Topologies topologies ^Cluster cluster]
(schedule-topologies-evenly topologies cluster))

它是通过调用schedule-topologies-evenly方法来完成任务分配的. 
schedule-topologies-evenly方法的具体定义如下:

(defn schedule-topologies-evenly [^Topologies topologies ^Cluster cluster]
;;通过调用cluster对象的needsSchedulingTopologies方法来获取所有需要进行任务调度的Topology集合,
;;needsSchedulingTopologies方法具体定义如fn1所示.
;;判断Topology是否需要进行任务调度的依据在fn2中有说明.
(let [needs-scheduling-topologies (.needsSchedulingTopologies cluster topologies)]
(doseq [^TopologyDetails topology needs-scheduling-topologies
;;对需要进行任务调度的Topology中的每一个,首先获取它的topology-id,
:let [topology-id (.getId topology)
;;调用schedule-topology方法获取计算得到的<executor,node+port>类型集合new-assignment
;;schedule-topology方法具体定义如fn3所示.
new-assignment (schedule-topology topology cluster)
;;将new-assignment的键和值颠倒获取<node+port,executors>集合.
node+port->executors (reverse-map new-assignment)]]
;;对于前面获取的<node+port,executors>集合中的每一项进行以下操作.
(doseq [[node+port executors] node+port->executors
;;用node和port信息构造WorkerSlot对象,并将其作为slot
:let [^WorkerSlot slot (WorkerSlot. (first node+port) (last node+port))
;;下面两行代码:对于executors集合中的每一项,构造ExecutorDetail对象,
;;并返回一个ExecutorDetails集合作为executors
executors (for [[start-task end-task] executors]
(ExecutorDetails. start-task end-task))]]
;;调用cluster的assign方法将计算出来的slot分配给与该Topology相对应的executors
(.assign cluster slot topology-id executors)))))

fn1:

/**
* 获取所有需要调度的Topology,并以集合的形式返回
*/
public List<TopologyDetails> needsSchedulingTopologies(Topologies topologies) {
List<TopologyDetails> ret = new ArrayList<TopologyDetails>();
for (TopologyDetails topology : topologies.getTopologies()) {
if (needsScheduling(topology)) {
ret.add(topology);
}
}
return ret;
}

fn2:

/**
* 判断Topology是否需要进行任务调度的依据有两个:
* 1.Topology设置的NumWorkers数目是否大于已经分配给Topology的Worker数目
* 2.该Topology尚未分配的Executor的数目是否大于0
*/
public boolean needsScheduling(TopologyDetails topology) {
int desiredNumWorkers = topology.getNumWorkers();
int assignedNumWorkers = this.getAssignedNumWorkers(topology); if (desiredNumWorkers > assignedNumWorkers) {
return true;
} return this.getUnassignedExecutors(topology).size() > 0;
}

fn3:

;;该方法会根据集群当前的可用资源对Topology进行任务分配
(defn- schedule-topology [^TopologyDetails topology ^Cluster cluster]
;;获取topology-id
(let [topology-id (.getId topology)
;;调用cluster的getAvailableSlots方法获取集群当前可用的slot资源,
;;将其转换为<node,port>集合并赋值给available-slots
;;getAvailableSlots主要负责计算当前集群中还没有使用的Supervisor端口
available-slots (->> (.getAvailableSlots cluster)
(map #(vector (.getNodeId %) (.getPort %))))
;;调用getExecutors获取Topology的所有Executor信息,
;;将其转换为<start-task-id,end-task-id>集合,
;;然后赋值给all-executors并返回
all-executors (->> topology
.getExecutors
(map #(vector (.getStartTask %) (.getEndTask %)))
set)
;;调用get-alive-assigned-node+port->executors方法(具体定义如fn3_1)
;;计算当前该Topology已经分得的资源情况,
;;最后返回一个<node+port,executors>集合并将其赋值给变量alive-assigned
;;参数为cluster信息和topology-id
alive-assigned (get-alive-assigned-node+port->executors cluster topology-id)
;;计算当前Topology可以使用的slot数目,并将其赋予total-slots-to-use,
;;该值的具体内容为下面两个值的最小值:
;;1.Topology中设置的Worker数目
;;2.当前available-slots加上alive-assigned数目
total-slots-to-use (min (.getNumWorkers topology)
(+ (count available-slots) (count alive-assigned)))
;;对available-slots进行排序,计算需要分配的slot数目(total-slots-to-use减去alive-assigned)
;;最后从排序后的available-slots集合中按顺序去除这些slot并赋值给reassign-slots
reassign-slots (take (- total-slots-to-use (count alive-assigned))
(sort-slots available-slots))
;;通过比较all-executors跟已经分配的Executor集合间的差异,获取需要进行分配的Executor集合
reassign-executors (sort (set/difference all-executors (set (apply concat (vals alive-assigned)))))
;;将上述计算得到的reassign-executors与reassign-slots进行关联,转换为<executor,slot>映射集合,
;;并赋值给reassignment,此时有两种情况:
;;1.reassign-executors数目少于reassign-slots数目:意味着当前集群中的可用资源比较多,
;;eg.reassign-executors为(e1,e2,e3),reassign-slots为(s1,s2,s3,s4,s5),
;;那么匹配结果为{[e1,s1],[e2,s2],[e3,s3]}
;;2.reassign-executors数目多于reassign-slots数目:意味着当前集群的可用资源非常有限,
;;eg.reassign-executors为(e1,e2,e3,e4,e5,e6),reassign-slots为(s1,s2),
;;此时会有多个Executor被分配到同一个slot上,返回的结果可能是:
;;{[e1,s1],[e2,s1],[e3,s2],[e4,s1],[e5,s2],[e6,s2]}
reassignment (into {}
(map vector
reassign-executors
;; for some reason it goes into infinite loop without limiting the repeat-seq
(repeat-seq (count reassign-executors) reassign-slots)))]
;;判断reassignment是否为空,若不为空则打印内容为可用的slot信息的日志
(when-not (empty? reassignment)
(log-message "Available slots: " (pr-str available-slots))
)
;;返回计算得到类型为<executor,[node,port]>的集合reassignment,
reassignment))

fn3_1:

;;该方法用于获取Topology当前已经分配得到的资源
(defn get-alive-assigned-node+port->executors [cluster topology-id]
;;调用cluster的getAssignmentById获取该Topology当前的assignment
(let [existing-assignment (.getAssignmentById cluster topology-id)
;;判断当前的assignment是否为空,若不为空,则获取其中的<executor,slot>信息
executor->slot (if existing-assignment
(.getExecutorToSlot existing-assignment)
{})
;;将前面获取到的<executor,slot>转换为<executor,[node+port]>集合
executor->node+port (into {} (for [[^ExecutorDetails executor ^WorkerSlot slot] executor->slot
:let [executor [(.getStartTask executor) (.getEndTask executor)]
node+port [(.getNodeId slot) (.getPort slot)]]]
{executor node+port}))
;;将前面的<executor,[node+port]>集合转换为<[node+port],executors>集合
alive-assigned (reverse-map executor->node+port)]
;;返回得到的<[node+port],executors>集合
alive-assigned))

注:学习李明老师等Storm源码分析和陈敏敏老师等Storm技术内幕与大数据实践的笔记整理。 
欢迎关注下面二维码进行技术交流: 

JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler的更多相关文章

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

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

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

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

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

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

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

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

  5. scrapy 源码解析 (四):启动流程源码分析(四) Scheduler调度器

    Scheduler调度器 对ExecutionEngine执行引擎篇出现的Scheduler进行展开.Scheduler用于控制Request对象的存储和获取,并提供了过滤重复Request的功能. ...

  6. Duilib源码分析(四)绘制管理器—CPaintManagerUI—(前期准备四)

    接下来,分析uilib.h中的WinImplBase.h和UIManager.h: WinImplBase.h:窗口实现基类,已实现大部分的工作,基本上窗口类均可直接继承该类,可发现该类继承于多个类, ...

  7. Duilib源码分析(四)绘制管理器—CPaintManagerUI—(前期准备二)

    接下来,我们继续分析UIlib.h文件中余下的文件,当然部分文件可能顺序错开分析,这样便于从简单到复杂的整个过程的里面,而避免一开始就出现各种不理解的地方. 1. UIManager.h:UI管理器, ...

  8. Duilib源码分析(四)绘制管理器—CPaintManagerUI—(前期准备一)

    上节中提到在遍历创建控件树后,执行了以下操作:      1. CDialogBuilder构建各控件对象并形成控件树,并返回第一个控件对象pRoot:     2. m_pm.AttachDialo ...

  9. Duilib源码分析(四)绘制管理器—CPaintManagerUI

    接下来,分析uilib.h中的UIManager.h,在正式分析CPaintManagerUI前先了解前面的一些宏.结构: 枚举类型EVENTTYPE_UI:定义了UIManager.h中事件通告类型 ...

随机推荐

  1. ADO.NET中的DataSet和DataReader

    ADO.NET提供两个对象用于检索关系型数据并把它存储在内存中,分别是DataSet和DataReader.DataSet提供内存中关系数据的表现--包括表和次序.约束等表间的关系的完整数据集合.Da ...

  2. PHP:win7 ASP.NET环境与PHP(WAMP)环境如何共存

    经验地址:http://jingyan.baidu.com/article/495ba8410f794d38b30ede89.html 笔记本以前安装过asp.net,启用了Windows的IIS服务 ...

  3. [0] (VDP)垂直开发模式

    垂直管理 网格化管理 属地管理 横向管理 面向接口编程 面向对象编程(OOP) 设计模式(GOF.MVC.SOA) 依赖注入(DI/IoC) 面向方面编程(AOP) 领域驱动开发(DDD) 测试驱动开 ...

  4. ArcGIS Earth(原谷歌地球)如何获取高精度矢量地图数据?(shp文件/要素类/kml)

    大家好,这次来分享干货.做地理分析的同学,或者需要使用地图却不知道哪里有矢量数据的时候,怎么办呢? 这次,我就告诉大家哪里能自己手工制作矢量点线面数据!注意哦,是自己绘制的. 使用到的软件: ArcG ...

  5. 360安全检测出的WordPress漏洞的修复方法

    1.跨站脚本攻击(XSS) 这个漏洞注意是因为用户评论可以提交代码,有安全风险.虽然你的WordPress以及是最新版,但是你的WordPress主题却不一定跟着更新!因此,需要稍微修改一下评论相关的 ...

  6. 盒子模型,定位技术,负边距,html5 新增标签

    盒子模型 /*[margin 外边距] margin属性最多四个 1.只写一个值,四个方向的margin均为这个值 2.写两个值:上,右两个方向,下默认=上,右 默认=左 3.写三个值:上.右.下三个 ...

  7. java 动态代理的实现

    http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

  8. 【Android Developers Training】 92. 序言:使用同步适配器传输数据

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  9. accp8.0转换教材第11章JAjax加护扩展理解与练习

    ①杂记:前面有原生态JavaScript实现ajax这里又多了更简单的方法实现ajax ②$.get()方法的常用参数 参数 类型 说明 url String 必选,规定发送地址 data Plain ...

  10. web前段学习2017.6.13

    CSS---表现层,修饰和表现html文档,为了解决结构层和表现层分离的问题. 通过CSS极大的提高了工作效率,方便工作人员维护和管理CSS:层叠样式表,目前用的最广泛的css版本为css2,最新版本 ...