JStorm与Storm源码分析(三)--Scheduler,调度器
Scheduler作为Storm的调度器,负责为Topology分配可用资源。
Storm提供了IScheduler接口,用户可以通过实现该接口来自定义Scheduler。
其定义如下:
public interface IScheduler {
//接收当前Nimbus的Storm配置作为参数,进行一些初始化工作
void prepare(Map conf); /**
* 真正进行任务分配的方法,在Nimbus进行任务分配的时候会调用该方法.
* 参数为topologies、cluster:前者含有当前集群中所有Topology的静态信息,
* cluster包含了Topology的运行态信息,比如用户自定义调度逻辑时所需要的所有资源、
* Supervisor信息、当前可用的所有slot
* 以及任物分配情况等,根据topologies和cluster信息,就可以进行调度分配任务了
*/
void schedule(Topologies topologies, Cluster cluster);
}
真正选择哪个调度器来对Topology进行分配的方法是mk-assignments。
mk-assignments方法定义与解释如下:
;;参数:stormConf和接口INimbus的实现类实例
(defn mk-scheduler [conf inimbus]
;;调用inimbus中的getForcedScheduler方法,并将返回值赋给临时变量forced-scheduler
(let [forced-scheduler (.getForcedScheduler inimbus)
scheduler (cond
;;若调用的getForcedScheduler方法,返回的是非null的IScheduler,则返回该IScheduler实例
forced-scheduler
(do (log-message "Using forced scheduler from INimbus " (class forced-scheduler))
forced-scheduler)
;;如果用户实现了自定义的IScheduler,并且在storm.yaml中有配置,
;;则返回用户自定义的IScheduler.
(conf STORM-SCHEDULER)
(do (log-message "Using custom scheduler: " (conf STORM-SCHEDULER))
(-> (conf STORM-SCHEDULER) new-instance))
;;如果上述都不满足则返回默认的DefaultScheduler
:else
(do (log-message "Using default scheduler")
(DefaultScheduler.)))]
(.prepare scheduler conf)
scheduler
))
从上述代码可以看出,如果调用inimbus中的getForcedScheduler方法,且返回的是非null的IScheduler,则返回该IScheduler实例;如果用户实现了自定义的IScheduler,并且在storm.yaml中有配置,则返回用户自定义的IScheduler;如果两者都没有实现,则采用默认调度器DefaultScheduler进行任务的分配。现在我们只关心DefaultScheduler。
DefaultScheduler的定义与解释如下:
;;DefaultScheduler是Storm默认的调度器,如果用户没有指定自己实现的调度器,
;;Storm就会使用该调度器进行Topology的任务分配。
;;DefaultScheduler实现了IScheduler接口
(ns backtype.storm.scheduler.DefaultScheduler
(:use [backtype.storm util config])
(:require [backtype.storm.scheduler.EvenScheduler :as EvenScheduler])
(:import [backtype.storm.scheduler IScheduler Topologies
Cluster TopologyDetails WorkerSlot SchedulerAssignment
EvenScheduler ExecutorDetails])
(:gen-class
:implements [backtype.storm.scheduler.IScheduler]))
;;default-schedule方法主要是计算当前集群中所有可供分配的slot资源,
;;并判断当前已经分配给该Topology的slot资源是否需要重新分配,
;;利用这些信息,对新提交的Topology进行资源分配
(defn default-schedule [^Topologies topologies ^Cluster cluster]
;;调用cluster的needsSchedulingTopologies方法获取所需要进行任务调度的Topology集合
;;needsSchedulingTopologies方法定义如fn1所示.
(let [needs-scheduling-topologies (.needsSchedulingTopologies cluster topologies)]
;;这部分代码块的作用是对每一个需要进行任务调度的Topology进行处理
(doseq [^TopologyDetails topology needs-scheduling-topologies
;;通过调用getId获取topology-id
:let [topology-id (.getId topology)
;;调用cluster的getAvailableSlots方法获取当前集群中所有可用的slot资源,
;;并将其转换为<node,port>集合赋给available-slots变量.
;;getAvailableSlots方法定义如下fn2所示
available-slots (->> (.getAvailableSlots cluster)
(map #(vector (.getNodeId %) (.getPort %))))
;;调用getExecutors获取Topology的所有Executor信息,
;;并将其转换为<start-task-id,end-task-id>集合
all-executors (->> topology
.getExecutors
(map #(vector (.getStartTask %) (.getEndTask %)))
set)
;;调用EvenScheduler的get-alive-assigned-node+port->executors方法
;;计算当前Topology已经分配的任务信息,以<[node,port],executors>信息保存到alive-assigned变量中
alive-assigned (EvenScheduler/get-alive-assigned-node+port->executors cluster topology-id)
;;
alive-executors (->> alive-assigned vals (apply concat) set)
;;调用slots-can-reassign方法对alive-assigned的slot信息进行判断,
;;选出其中可被重新分配的slot集合并保存到can-reassign-slots.
;;slots-can-reassign方法定义如fn3所示:
can-reassign-slots (slots-can-reassign cluster (keys alive-assigned))
;;计算当前Topology所能使用的全部slot数目,它取以下两个量中较小的值作为total-slots-to-use
total-slots-to-use (min (.getNumWorkers topology)
(+ (count can-reassign-slots) (count available-slots)))
;;用于判断如果total-slots-to-use的数目大于当前已经分配的slot数目,
;;或者正在运行的executors数目不等于所有的executors数
;;则调用bad-slots方法计算所有可被释放的slot.
;;bad-slots方法的具体定义如fn4所示.
bad-slots (if (or (> total-slots-to-use (count alive-assigned))
(not= alive-executors all-executors))
(bad-slots alive-assigned (count all-executors) total-slots-to-use)
[])]]
;;调用cluster的freeSlots方法释放前面计算出来的bad-slots
(.freeSlots cluster bad-slots)
;;调用EvenScheduler的schedule-topologies-evenly方法将系统中的资源均匀分配给Topology
(EvenScheduler/schedule-topologies-evenly (Topologies. {topology-id topology}) cluster))))
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:
//根据supervisor信息获取所有可用的slot资源,并封装在WorkerSlot中,以集合的形式返回
public List<WorkerSlot> getAvailableSlots(SupervisorDetails supervisor) {
Set<Integer> ports = this.getAvailablePorts(supervisor);
List<WorkerSlot> slots = new ArrayList<WorkerSlot>(ports.size());
for (Integer port : ports) {
slots.add(new WorkerSlot(supervisor.getId(), port));
}
return slots;
}
fn3:
;;该方法将对传入的slots资源进行过滤,选出其中仍然可以继续使用的slot,组成新的集合
;;过滤方法:先判断slot的node信息是否存在于集群的黑名单里,
;;如果不在则继续判断slot的port信息是否在于node相对应的Supervisor的所有可用端口列表中
;;如果在,则表示该slot可以继续使用
(defn slots-can-reassign [^Cluster cluster slots]
(->> slots
(filter
(fn [[node port]]
(if-not (.isBlackListed cluster node)
(if-let [supervisor (.getSupervisorById cluster node)]
(.contains (.getAllPorts supervisor) (int port))
))))))
fn4:
;;该方法用于计算一个Topology已经分配的资源中哪些是不再需要的
;;existing-slots:已经分配出去的资源(分配给Topology),它是一个<[node,port],executors>集合
;;num-executors:Topology的所有Executor(包括已分配和未分配的)
;;num-workers:Topology可使用的全部slot数目
(defn- bad-slots [existing-slots num-executors num-workers]
;;判断num-workers是否为0。如果是,意味着当前没有可供该Topology使用的slot,这时返回一个空集合
(if (= 0 num-workers)
'()
;;定义distribution集合和keepers集合,distribution集合通过调用integer-divided方法生成
;;实际所做的事是将num-executors均匀地分配到num-workers中.
;;keepers集合为一个空集合
(let [distribution (atom (integer-divided num-executors num-workers))
keepers (atom {})]
;;对于传入的existing=slots中的每一项,计算其对象的executor-count,
;;然后以该executor-count作为键从前面计算的distribution集合中获取值.如果获取的值大于0,
;;则意味着存在至少一个Worker上有executor-count个Executor的分配,并且,这个分配信息便继续维持,不更新。
;;这时,会将<[node,port],executors>信息放入keepers中,同时将distribution中该executor-count的对应值减一.
(doseq [[node+port executor-list] existing-slots :let [executor-count (count executor-list)]]
(when (pos? (get @distribution executor-count 0))
(swap! keepers assoc node+port executor-list)
(swap! distribution update-in [executor-count] dec)
))
;;从existing-slots中移除keepers中记录的需要继续维持的分配情况.如果移除完之后还存在slot信息,
;;表明这些slot可以被释放掉,将其转换为WorkerSlot对象集合并返回.
(->> @keepers
keys
(apply dissoc existing-slots)
keys
(map (fn [[node port]]
(WorkerSlot. node port)))))))
注:学习李明等老师Storm源码分析和陈敏敏等老师Storm技术内幕与大数据实践的笔记整理。
欢迎关注下面二维码进行技术交流:
JStorm与Storm源码分析(三)--Scheduler,调度器的更多相关文章
- JStorm与Storm源码分析(一)--nimbus-data
Nimbus里定义了一些共享数据结构,比如nimbus-data. nimbus-data结构里定义了很多公用的数据,请看下面代码: (defn nimbus-data [conf inimbus] ...
- JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler
EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口, 由下面代码可以看出: (ns backtype.storm.scheduler.EvenSche ...
- JStorm与Storm源码分析(二)--任务分配,assignment
mk-assignments主要功能就是产生Executor与节点+端口的对应关系,将Executor分配到某个节点的某个端口上,以及进行相应的调度处理.代码注释如下: ;;参数nimbus为nimb ...
- Duilib源码分析(三)XML解析器—CMarkup
上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI,默认为UTF ...
- scrapy 源码解析 (四):启动流程源码分析(四) Scheduler调度器
Scheduler调度器 对ExecutionEngine执行引擎篇出现的Scheduler进行展开.Scheduler用于控制Request对象的存储和获取,并提供了过滤重复Request的功能. ...
- JStorm与Storm源码分析(五)--SpoutOutputCollector与代理模式
本文主要是解析SpoutOutputCollector源码,顺便分析该类中所涉及的设计模式–代理模式. 首先介绍一下Spout输出收集器接口–ISpoutOutputCollector,该接口主要声明 ...
- Storm源码分析--Nimbus-data
nimbus-datastorm-core/backtype/storm/nimbus.clj (defn nimbus-data [conf inimbus] (let [forced-schedu ...
- storm源码分析之任务分配--task assignment
在"storm源码分析之topology提交过程"一文最后,submitTopologyWithOpts函数调用了mk-assignments函数.该函数的主要功能就是进行topo ...
- tomcat源码分析(三)一次http请求的旅行-从Socket说起
p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...
随机推荐
- placeholder属性兼容ie8
<!doctype html> <html> <head> <meta charset="utf-8" /> <title&g ...
- 使用hashCode()和equals()方法 - Java
在这篇文章中,我将指出我对hashCode()和equals()方法的理解.我将讨论它们的默认实现以及如何正确地覆盖它们.我还将使用Apache Commons包中的实用工具类来实现这些方法. has ...
- 本地存储 web storage
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Mac远程连接windows报错“证书或相关链无效,是否仍要连接到此计算机”的处理办法。
这个主要是因为策略组设置的问题.详细的设置方法如下: 本地计算机策略>计算机配置>管理模板>windows组件>远程桌面服务>远程桌面会话主机>安全>远程(R ...
- 自定义分布式RESTful API鉴权机制
微软利用OAuth2为RESTful API提供了完整的鉴权机制,但是可能微软保姆做的太完整了,在这个机制中指定了数据持久化的方法是用EF,而且对于用户.权限等已经进行了封装,对于系统中已经有了自己的 ...
- ecshop和jQuery冲突
这个问题看ecshop的论坛里有很多帖子,解决方案就好几种,但是有几个标注完美解决方案的需要更改很多文件,对于我们这种初学者出现了问题的话是不知道怎么调试的. 找到一个很简单的解决方案,论坛里说只能解 ...
- 使用websocket-sharp来创建c#版本的websocket服务
当前有一个需求,需要网页端调用扫描仪,javascript不具备调用能力,因此需要在机器上提供一个ws服务给前端网页调用扫描仪.而扫描仪有一个c#版本的API,因此需要寻找一个c#的websocket ...
- 细说Nullable<T>类型
目录一.简介二.语法和用法三.类型的转换和运算四.装箱与拆箱五.GetType()方法六.ToString()方法七.System.Nullable帮助类八.语法糖 一.简介 众所周知,值类型变量不能 ...
- Maven转化为Dynamic Web Module
如今Maven仍然是最常用的项目管理工具,若要将Java Web项目使用Maven进行管理,则首先需要新建Maven项目,然后将其转化为web项目. 在项目右键选择properties,然后点击左侧P ...
- 大话Session
[原创]转载请保留出处:shoru.cnblogs.com 晋哥哥的私房钱 引言 在web开发中,session是个非常重要的概念.在许多动态网站的开发者看来,session就是一个变量,而且其表现像 ...