分布式调度

116 阅读5分钟

分布式调度问题

分布式调度含义

  1. 运行在分布式集群环境下的调度任务。(任务程序集群,增强健壮性,横向拆分
  2. 对调度任务的分布式拆分。(任务程序的解耦,把大的任务拆分成多个小的作业任务,同时执行,纵向拆分

定时任务与消息队列的区别

  • 共同点
    1. 异步处理
    2. 应用解耦
    3. 流量削峰
  • 不同点
    1. 定时任务是时间驱动,MQ 是事件驱动
    2. 定时任务更倾向于批处理,MQ 倾向于逐条处理

分布式调度框架 Elastic-Job

项目组成:

  1. Elastic-Job-Lite:轻量级无中心化解决方案,使用Jar包的形式提供分布式任务的协调服务
  2. Elastic-Job-Cloud:需结合 Mesos 以及 Docker 在云环境下使用

主要功能:

  1. 分布式调度协调:在分布式环境中,任务能够按指定的调度策略执⾏,并且能够避免同⼀任务多实例重复执⾏
  2. 丰富的调度策略:基于成熟的定时任务作业框架 Quartz cron 表达式执⾏定时任务
  3. 弹性扩容缩容:当集群中增加某⼀个实例,它应当也能够被选举并执⾏任务;当集群减少⼀个实例 时,它所执⾏的任务能被转移到别的实例来执⾏。
  4. 失效转移:某实例在任务执⾏失败后,会被转移到其他实例执⾏
  5. 错过执行作业重触发:若因某种原因导致作业错过执⾏,⾃动记录错过执⾏的作业,并在上次作业完成后⾃动触发。
  6. 支持并行调度:⽀持任务分⽚,任务分⽚是指将⼀个任务分为多个⼩任务项在多个实例同时执⾏。
  7. 作业分片一致性:当任务被分⽚后,保证同⼀分⽚在分布式环境中仅⼀个执⾏实例。

Elastic-Job-Lite 简单使用

轻量级:jar包 + zookeeper

使用步骤

  1. 安装 Zookeeper

  2. 引入依赖

    <dependency>
        <groupId>com.dangdang</groupId>
        <artifactId>elastic-job-lite-core</artifactId>
        <version>2.1.5</version>
    </dependency>
    
  3. API 简单调用

    • 自定义 XxxJob 类实现 SimpleJob,重写 execute 方法
    • 配置注册中心 zookeeper
    • 配置任务
    • 启动任务
    // 配置注册中⼼zookeeper,zookeeper协调调度,不能让任务重复执⾏,通过命名空间分类管理任务,对应到zookeeper的⽬录
    ZookeeperConfiguration zookeeperConfiguration = new
    ZookeeperConfiguration("localhost:2181","namespace");
    CoordinatorRegistryCenter coordinatorRegistryCenter = new
    ZookeeperRegistryCenter(zookeeperConfiguration); coordinatorRegistryCenter.init();
    
    // 配置任务(时间事件、定时任务业务逻辑、调度器)
    JobCoreConfiguration jobCoreConfiguration = JobCoreConfiguration.newBuilder("Xxx-job","*/2 * * * * ?",1).build();
    
    SimpleJobConfiguration simpleJobConfiguration = new
    SimpleJobConfiguration(jobCoreConfiguration, XxxJob.class.getName());
    
    // 启动任务
    new JobScheduler(coordinatorRegistryCenter, LiteJobConfiguration.newBuilder(simpleJobConfiguration).build()).init();
    

Elastic-Job-Lite轻量级去中⼼化的特点

去中心化:

  1. 执行节点对等
  2. 定时调度自触发(没有中心调度节点)
  3. 服务自发现(通过注册中心)
  4. 主节点非固定(Leader节点选举机制,当集群启动或Leader 节点下线,会为任务重新选举Leader)

轻量级:

  1. All in jar,必要依赖仅仅只有 zookeeper
  2. 并非独立部署的中间件,就是 jar 程序

任务分片

分片逻辑:

start=>start: 开始
getInstance=>operation: 获取有效的作业实例服务器
sharding?=>condition: 是否需要分片
overOneServer=>condition: 超过一台有效服务器
leader?=>condition: 是否为主节点
leaderExist?=>condition: 主节点是否存在
shardingFinish?=>condition: 分片是否完成
waitingShardingTask=>operation: 等待当前任务其他分片运行结束
waitingLeaderSelect=>operation: 等待主节点选举
waitingSharding=>operation: 等待主节点分片完成
sharding=>operation: 执行分片逻辑
end=>end: 结束

start->getInstance->sharding?
sharding?(yes,bottom)->overOneServer
sharding?(no,left)->end
overOneServer(yes,bottom)->leader?
overOneServer(no,left)->end
leader?(yes,bottom)->waitingShardingTask->sharding->end
leader?(no)->leaderExist?
leaderExist?(yes,right)->shardingFinish?
leaderExist?(no)->waitingLeaderSelect(right)->shardingFinish?
shardingFinish?(yes,right)->end
shardingFinish?(no,)->waitingSharding->end

ElasticJob 可以把作业分为多个 task(每⼀个task就是⼀个任务分⽚),每 ⼀个task交给具体的⼀个机器实例去处理(⼀个机器实例是可以处理多个task的),但是具体每个task 执⾏什么逻辑由我们⾃⼰来指定.

Strategy策略定义这些分⽚项怎么去分配到各个机器上去,默认是平均去分,可以定制,⽐如某⼀个机器负载 ⽐较⾼或者预配置⽐较⾼,那么就可以写策略。分⽚和作业本身是通过⼀个注册中⼼协调的,因为在分布式环境下,状态数据肯定集中到⼀点,才可以在分布式中沟通。

举例:

可在配置任务的时候设置分片数量,并通过 shardingItemParmeters 方法设置分片序号与携带参数,在 execute 方法中通过 shardingContext.getShardingParameter() 获取分片参数,针对不同参数,对处理对应任务,达到简单分片的效果

弹性扩容

当增加实例,它会⾃动注册到注册中⼼,注册中⼼发现新的服务上线,注册中⼼会通知 ElasticJob 进⾏重新分⽚

1)分⽚项也是⼀个JOB配置,修改配置,重新分⽚,在下⼀次定时运⾏之前会重新调⽤分⽚算法,那么这个分⽚算法的结果就是:哪台机器运⾏哪⼀个⼀⽚,这个结果存储到zk中的,主节点会把分⽚给分好放到注册中⼼去,然后执⾏节点从注册中⼼获取信息(执⾏节点在定时任务开启的时候获取相应的分⽚)。

2)如果所有的节点挂掉值剩下⼀个节点,所有分⽚都会指向剩下的⼀个节点,这也是ElasticJob的⾼可⽤。