Storm基础篇三—Scheduler、Configuration

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

Scheduler

概述: Scheduler是Storm中的调度器,负责为topology分配当前集群中可用的资源。Storm定义了IScheduler接口, 用户可以通过实现该接口来定义自己的Scheduler。Storm有四种内置的调度器,包含: DefaultSchedulerIsolationSchedulerMultitenantSchedulerResourceAwareScheduler.

Topology的提交过程:

  1. 非本地模式下,客户端通过thrift调用nimbus接口,来上传代码到nimbus并触发提交操作。
  2. nimbus进行任务分配,并将信息同步到zookeeper。
  3. supervisor定期获取任务分配信息,如果topology代码缺失,会从nimbus下载代码,并根据任务分配信息,同步worker。
  4. worker根据分配的tasks信息,启动多个executor线程,同时实例化spout、bolt、acker等组件,此时,等待所有connections(worker和其它机器通讯的网络连接)启动完毕,storm集群即进入工作状态。
  5. 除非显示调用kill topology,否则spout、bolt等组件会一直运行。

DefaultScheduler(Storm默认调度器):

主要源码分析:

   public static void defaultSchedule(Topologies topologies, Cluster cluster) {
        for (TopologyDetails topology : cluster.needsSchedulingTopologies()) {  //调用cluster对象的needsSchedulingTopologys方法获取需要进行任务调度的Topology集合.
            
            //调用cluster的getAvailableSlots方法获取当前集群可用的slot资源集合(availableSlots)
            List<WorkerSlot> availableSlots = cluster.getAvailableSlots();
            Set<ExecutorDetails> allExecutors = topology.getExecutors();
            
            //调用getAliveAssignedWorkerSlotExecutors方法获取当前topology已经分配的资源情况
            Map<WorkerSlot, List<ExecutorDetails>> aliveAssigned =
                EvenScheduler.getAliveAssignedWorkerSlotExecutors(cluster, topology.getId());
                
            //将topology中的ExecutorDetails集合存入aliveExecutors Set集合中
            Set<ExecutorDetails> aliveExecutors = new HashSet<ExecutorDetails>();
            for (List<ExecutorDetails> list : aliveAssigned.values()) {
                aliveExecutors.addAll(list);
            }
            
            //调用slotsCanReassign方法对aliveAssigned的slot信息进行判断,选择其中可被重新分配的slot集合并保存到 canReassignSlots 集合中
            Set<WorkerSlot> canReassignSlots = slotsCanReassign(cluster, aliveAssigned.keySet());
            
            //计算当前Topology所能使用的全部slot的数目,topology设置的worker数目与当前availableSlots数目加上canReassignSlots数据二者的最小值(totalSlotsToUse)
            int totalSlotsToUse = Math.min(topology.getNumWorkers(), canReassignSlots.size() + availableSlots.size());

            //判断totalSlotsToUse的数目是否大于当前已分配的slot数目(aliveAssigned),若大于则调用badSlots方法计算所有可能被释放的slot
            Set<WorkerSlot> badSlots = null;
            if (totalSlotsToUse > aliveAssigned.size() || !allExecutors.equals(aliveExecutors)) {
                badSlots = badSlots(aliveAssigned, allExecutors.size(), totalSlotsToUse);
            }
            
            //调用cluster的freeSlots方法释放前面计算出来的badSlots
            if (badSlots != null) {
                cluster.freeSlots(badSlots);
            }

            //调用EventScheduler的scheduleTopologiesEvenly方法将系统中的资源均匀分配该Topology
            EvenScheduler.scheduleTopologiesEvenly(new Topologies(topology), cluster);
        }
    }
复制代码

Pluggable scheduler(可插拔式的任务调度器):

你可以实现自己的scheduler(调度器)来代替默认的调度器将executors分配给给worker。使用时,需在storm.yaml 文件中将 "storm.scheduler" 配置属性设置成你的class类, 并且你的scheduler必须实现 IScheduler 接口。

Isolation Scheduler(隔离调度器):

Isolation scheduler(隔离调度器)使得多个topologies共享的集群资源更加容易和安全。Isolation scheduler允许你指定某些 topologies是 “isolated”(隔离的),这就意味着这些被隔离的topologies运行在集群的特定机器上,这些机器上没有其他topologies运行。 被隔离的topologies具有高优先级,所以如果和 non-isolated topologies(非隔离的拓扑)竞争资源的话,资源将会分配给被隔离的topologies,甚至如果必须给被隔离的topologies分配资源,将会从那些非隔离的topologies中抽取资源。一旦所有被隔离的topologies所需资源得到满足, 那么集群中剩下的机器将会被非隔离的topologies所共享。

你可以通过将storm.scheduler设置为org.apache.storm.scheduler.IsolationScheduler,这样 Nimbus 节点的 Scheduler 就配置成了隔离调度器。然后,使用 isolation.scheduler.machines配置来指定每个 topology 分配多少台机器. 这个配置是一个 map 集合,K为 topology name,V为分配给此 topology 的隔离机器数量。示例如下:

storm.scheduler: "org.apache.storm.scheduler.IsolationScheduler"
isolation.scheduler.machines: 
    "my-topology": 8
    "tiny-topology": 1
    "some-other-topology": 3
复制代码

所有提交到集群中的 topologies 如果没有出现在上述 map 集合中,那么将不会被隔离。请注意:user不可以设置 isolation 属性,该配置只能通过集群的管理员分配(这是故意这样设计的)。

隔离调度器通过在拓扑之间提供完全的隔离来解决多租户问题,避免 topologies 之间的资源竞争问题。最终的目的是 "生产上"的 topologies 应该设置成被隔离的 , 测试或开发中的 topologies 则相反。集群上的其余机器承担着为被隔离的 topology 进行故障转移和运行非隔离 topology 的双重角色。

MultitenantScheduler:

这种调度模式会为每个 topology 发布者构造一个自己专属的隔离资源池,之后会通过遍历topology集,通过为资源池分配 topology 关联来分配节点。

ResourceAwareScheduler:

资源感知调度器可以在每个用户的基础上分配资源。每个用户可以保证一定数量的资源来运行他的 topology,并且资源感知调度器将尽可能满足这些保证。当Storm 群集具有额外的免费资源时,资源感知调度器将能够以公平的方式为用户分配额外的资源。

Configuration

Storm 有许多配置项用于调整 nimbus、supervisors 和 topologys 的行为。有些配置项是系统配置项,在topology 中不能修改,另外一些配置项则是可以在每个 topology 中修改的。

每个配置项在Storm代码库中的defaults.yaml (①)文件中都有一个默认值。你可以通过在 Nimbus 和 Supervisors 的环境变量中定义一个 storm.yaml (②)来覆盖默认值。最后,在你使用 StormSubmitter提交你的 topology 时也可以定义基于具体拓扑的配置项(③)。但是,基于具体拓扑的配置项仅能够覆盖那些以 “TOPOLOGY” 作为前缀的配置项。

Storm 0.7.0及以上的版本允许你覆写每个 bolt/spout 的配置,通过这种方式只能修改以下配置项:

  1. "topology.debug"
  2. "topology.max.spout.pending"
  3. "topology.max.task.parallelism"
  4. "topology.kryo.register": 由于在 topology 中序列化对所有组件均是可见的,所以这项配置与其它配置项略有不同,详情请见 Serialization

Storm 的 Java API 支持两种自定义组件配置信息的方式:

  1. Internally(内置)(④):  在 spout/bolt 中重写 getComponentConfiguration ,并返回组件特定的配置 map。
  2. Externally(外置)(⑤):  TopologyBuilder 中的 setSpout 与 setBolt 方法会返回一个带有 addConfiguration 方法的ComponentConfigurationDeclarer 对象,通过 addConfiguration 方法就可以重写对应组件的配置。

配置的优先级为 defaults.yaml < storm.yaml < topology specific configuration < internal component specific configuration < external component specific configuration。也就是上面标注的:① < ② < ③ < ④ < ⑤ .

Bolts, Spouts, and Plugins

在绝大多数情况下,bolts 或 spouts 的配置都应该通过 bolts 或 spouts 上的 setter 完成,而不是通过拓扑配置完成。在一些极少数的情况下,这可能会暴露一些当前不属于 ConfigDaemonConfig 一部分的拓扑的配置,例如在将自定义调度器或插件写入storm时。在这些情况下,您可以创建自己的类来实现Validated接口,比如Config。此类中声明的任何public static final String字段都将被视为配置项,org.apache.storm.validation.ConfigValidationAnnotations类中的注解可用于指定那些配置项应存储config中。要让validator知道这个类,你需要将该类视为将通过 ServiceLoader 为 Validated 类加载的服务,并在jar中包含一个META-INF/services/org.apache.storm.validation.Validated文件来保存你配置类的名称。

Resources:

该博客仅为初学者自我学习的记录,粗浅之言,如有不对之处,恳请指正。如有不解之处,欢迎评论,一起学习!

参考资料

Storm Document -> Scheduler
Storm Document -> Configuration
DefaultScheduler源码
Storm调度器介绍

猜你喜欢

转载自juejin.im/post/7017333638941392932