elastic-job-spring-boot

qq交流群:812321371

1 简介

Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-LiteElastic-Job-Cloud组成。Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务。

基于quartz定时任务框架为基础的,因此具备quartz的大部分功能

使用zookeeper做协调,调度中心,更加轻量级

支持任务的分片

支持弹性扩容,可以水平扩展, 当任务再次运行时,会检查当前的服务器数量,重新分片,分片结束之后才会继续执行任务

失效转移,容错处理,当一台调度服务器宕机或者跟zookeeper断开连接之后,会立即停止作业,然后再去寻找其他空闲的调度服务器,来运行剩余的任务

提供运维界面,可以管理作业和注册中心。

1.1 使用场景

由于项目为微服务,单模块可能在两个实例以上的数量,定时器就会出现多实例同时执行的情况。

一般定时器缺少管理界面,无法监控定时器是否执行成功。

市面上常见的解决方案为定时器加锁的操作,或者采用第3方分布式定时器。

分布式定时器有多种方案,比如阿里内部的ScheduledX,当当网的Elastic job,个人开源的xxl-job等。

1.2 功能列表

  • 分布式调度协调
  • 弹性扩容缩容
  • 失效转移
  • 错过执行作业重触发
  • 作业分片一致性,保证同一分片在分布式环境中仅一个执行实例
  • 自诊断并修复分布式不稳定造成的问题
  • 支持并行调度
  • 支持作业生命周期操作
  • 丰富的作业类型
  • Spring整合以及命名空间提供
  • 运维平台

1.3 概念

分片:任务的分布式执行,需要将一个任务拆分为多个独立的任务项,然后由分布式的服务器分别执行某一个或几个分片项。

例如:有一个遍历数据库某张表的作业,现有2台服务器。为了快速的执行作业,那么每台服务器应执行作业的50%。 为满足此需求,可将作业分成2片,每台服务器执行1片。作业遍历数据的逻辑应为:服务器A遍历ID以奇数结尾的数据;服务器B遍历ID以偶数结尾的数据。 如果分成10片,则作业遍历数据的逻辑应为:每片分到的分片项应为ID%10,而服务器A被分配到分片项0,1,2,3,4;服务器B被分配到分片项5,6,7,8,9,直接的结果就是服务器A遍历ID0-4结尾的数据;服务器B遍历ID5-9结尾的数据。

历史轨迹:Elastic-Job提供了事件追踪功能,可通过事件订阅的方式处理调度过程的重要事件,用于查询、统计和监控。

1.4 封装elasticjob

由于当当网Elastic job处于1年间未更新阶段,相关jar处于可以使用阶段功能不全。考虑到使用场景为多项目使用,将elastic-job-lite-spring简单封装便于使用。

2.使用说明:

2.1 添加依赖

ps:实际version版本请使用最新版

<dependency>
<groupId>com.purgeteam</groupId>
<artifactId>elasticjob-spring-boot-starter</artifactId>
<version>0.1.1.RELEASE</version>
</dependency>

2.2 配置

ps: 需要mysql,zookeeper支持,请提前搭建好。

配置bootstrap.yml或者application.yml

加入以下配置:

spring:
elasticjob:
datasource: # job需要的记录数据源
url: jdbc:mysql://127.0.0.1:3306/batch_log?useUnicode=true&characterEncoding=utf-8&verifyServerCertificate=false&useSSL=false&requireSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: Rtqw123OpnmER
regCenter: # 注册中心
serverList: 127.0.0.1:2181
namespace: elasticJobDemo

2.3 定时器实现方法编写

创建定时器类(唯一不同的地方在于将@Scheduled改为实现SimpleJob接口即可)

定时器实现方法编写在execute方法里。

@Slf4j
@Component
public class MySimpleJob implements SimpleJob { // @Scheduled(cron = "0 0/1 * * * ?")
@Override
public void execute(ShardingContext shardingContext) {
log.info(String.format("Thread ID: %s, 作业分片总数: %s, " +
"当前分片项: %s.当前参数: %s," +
"作业名称: %s.作业自定义参数: %s",
Thread.currentThread().getId(),
shardingContext.getShardingTotalCount(),
shardingContext.getShardingItem(),
shardingContext.getShardingParameter(),
shardingContext.getJobName(),
shardingContext.getJobParameter()
));
// 分片大致如下:根据配置的分片参数执行相应的逻辑
switch (context.getShardingItem()) {
case 0:
// do something by sharding item 0
break;
case 1:
// do something by sharding item 1
break;
case 2:
// do something by sharding item 2
break;
// case n: ...
}
}
}
log:Thread ID: 66, 作业分片总数: 1, 当前分片项: 0.当前参数: Beijing,作业名称: PropertiesSimpleJob.作业自定义参数: test

2.4 配置定时器

2.4.1 创建Configuration类

ZookeeperRegistryCenterJobEventConfiguration注入。

创建JobScheduler @Bean(initMethod = "init")

mySimpleJobScheduler方法里先通过ElasticJobUtils#getLiteJobConfiguration获取LiteJobConfiguration对象。

创建SpringJobScheduler对象返回即可。

@Configuration
public class MyJobConfig { // job 名称
private static final String JOB_NAME = "MySimpleJob"; // 定时器cron参数
private static final String CRON = "0 0/1 * * * ?"; // 定时器分片
private static final int SHARDING_TOTAL_COUNT = 1; // 分片参数
private static final String SHARDING_ITEM_PARAMETERS = "0=Beijing,1=Shanghai,2=Guangzhou"; // 自定义参数
private static final String JOB_PARAMETERS = "parameter"; @Resource
private ZookeeperRegistryCenter regCenter; @Resource
private JobEventConfiguration jobEventConfiguration; @Bean(initMethod = "init")
public JobScheduler mySimpleJobScheduler(final MySimpleJob mySimpleJob) { LiteJobConfiguration liteJobConfiguration = ElasticJobUtils
.getLiteJobConfiguration(mySimpleJob.getClass(), JOB_NAME, CRON,
SHARDING_TOTAL_COUNT, SHARDING_ITEM_PARAMETERS, JOB_PARAMETERS);
// 参数:1.定时器实例,2.注册中心类,3.LiteJobConfiguration,
// 3.历史轨迹(不需要可以省略)
return new SpringJobScheduler(mySimpleJob, regCenter, liteJobConfiguration, jobEventConfiguration);
} }

ElasticJobUtils#getLiteJobConfiguration参数简介:

/**
* 获取 {@link LiteJobConfiguration} 对象
*
* @param jobClass 定时器实现类
* @param jobName 定时器名称
* @param cron 定时参数
* @param shardingTotalCount 作业分片总数
* @param shardingItemParameters 当前参数 可以为null
* @param jobParameters 作业自定义参数 可以为null
* @return {@link LiteJobConfiguration}
*/
public static LiteJobConfiguration getLiteJobConfiguration(
final Class<? extends SimpleJob> jobClass,
final String jobName,
final String cron,
final int shardingTotalCount,
final String shardingItemParameters,
final String jobParameters) {
...
return ...;
}

2.4.2 简化Configuration类

当然也可以用下面的@Configuration实现简化,配置bootstrap.yml或者application.yml

spring:
elasticjob:
scheduled:
jobConfigMap: // 为map集合
PropertiesSimpleJob: // 定时器key名称
jobName: PropertiesSimpleJob // job名称
cron: 0 0/1 * * * ? // cron表达式
shardingTotalCount: 2 // 分片数量
shardingItemParameters: 0=123,1=332 // 分片参数
jobParameters: test // 自定义参数

注入SpringJobSchedulerFactory,在propertiesSimpleJobScheduler方法里调用gerSpringJobScheduler方法即可。

@Configuration
public class PropertiesSimpleJobConfig { @Resource
private SpringJobSchedulerFactory springJobSchedulerFactory; @Bean(initMethod = "init")
public JobScheduler propertiesSimpleJobScheduler(final PropertiesSimpleJob job) {
// 参数:1.定时器实例,2.配置名称,3.是否开启历史轨迹
return springJobSchedulerFactory.getSpringJobScheduler(job,"PropertiesSimpleJob", true);
} }

2.4.3 注解方式配置(推荐方式)

ps:这个注解包含了上述方式,简化定时器注入。

继承SimpleJob实现方法execute

AnnotationSimpleJob类上加入注解@ElasticJobScheduler即可。

下面为完整注解。

@Slf4j
@ElasticJobScheduler(
name = "AnnotationSimpleJob", // 定时器名称
cron = "0/8 * * * * ?", // 定时器表达式
shardingTotalCount = 1, // 作业分片总数 默认为1
shardingItemParameters = "0=Beijing,1=Shanghai,2=Guangzhou", // 分片序列号和参数用等号分隔 不需要参数可以不加
jobParameters = "123", // 作业自定义参数 不需要参数可以不加
isEvent = true // 是否开启数据记录 默认为true
)
public class AnnotationSimpleJob implements SimpleJob { @Override
public void execute(ShardingContext shardingContext) {
log.info(String.format("Thread ID: %s, 作业分片总数: %s, " +
"当前分片项: %s.当前参数: %s," +
"作业名称: %s.作业自定义参数: %s",
Thread.currentThread().getId(),
shardingContext.getShardingTotalCount(),
shardingContext.getShardingItem(),
shardingContext.getShardingParameter(),
shardingContext.getJobName(),
shardingContext.getJobParameter()
));
}
}

总结

分布式job可以解决多个项目同一个定时器都执行的问题,配合elastic-job控制台可以直观监控定时器执行情况等。

示例代码地址:elastic-job-spring-boot

作者GitHub:

Purgeyao 欢迎关注

本文由博客一文多发平台 OpenWrite 发布!

SpringBoot-ElasticJob封装快速上手使用(分布式定时器)的更多相关文章

  1. 简明易懂,将细节隐藏,面向新手树立web开发概念——学完Java基础语法,超快速上手springboot+mybatiJavaWeb开发

    简明易懂,将细节隐藏,面向新手树立web开发概念 --学完Java基础语法,超快速上手JavaWeb开发 Web本质(先忽视各种协议) Web应用可以理解为浏览器和服务器之间的交互. 我们可以看一个简 ...

  2. [Apache Pulsar] 企业级分布式消息系统-Pulsar快速上手

    Pulsar快速上手 前言 如果你还不了解Pulsar消息系统,可以先看上一篇文章 企业级分布式消息系统-Pulsar入门基础 Pulsar客户端支持多个语言,包括Java,Go,Pytho和C++, ...

  3. 【快学SpringBoot】快速上手好用方便的Spring Cache缓存框架

    前言 缓存,在开发中是非常常用的.在高并发系统中,如果没有缓存,纯靠数据库来扛,那么数据库压力会非常大,搞不好还会出现宕机的情况.本篇文章,将会带大家学习Spring Cache缓存框架. 原创声明 ...

  4. SpringBoot+SpringDataJpa快速上手(基本CRUD)

    以及表结构和数据 依赖 <!-- 如果有SpringBoot启动器,就不加--> <parent> <groupId>org.springframework.boo ...

  5. SpringBoot 2.X 快速掌握

    0.重写博文的原因 当初我的SpringBoot系列的知识是采用分节来写的,即:每一个知识点为一篇博文,但是:最近我霉到家了,我发现有些博文神奇般地打不开了,害我去找当初的markdown笔记,但是方 ...

  6. 想要快速上手 Spring Boot?看这些教程就足够了!| 码云周刊第 81 期

    原文:https://blog.gitee.com/2018/08/19/weekly-81/ 想要快速上手 Spring Boot?看这些教程就足够了!| 码云周刊第 81 期 码云周刊 | 201 ...

  7. Hortonworks,快速上手 Hadoop 的套件

    最近我在思考的一件事情:如何帮助团队 SQL 开发快速掌握大数据相关技术呢?面对疯狂暴涨的数据,SQL Server 存储成本越来越高了,日志的增长量也极大超过预期,隔三差五总有空间不足导致的应用异常 ...

  8. Elastic Search快速上手(1):简介及安装配置

    前言 最近开始尝试学习Elastic Search,因此决定做一些简单的整理,以供后续参考,快速上手使用ES. 简介 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多 ...

  9. Git版本控制Windows版快速上手

    说到版本控制,之前用过VSS,SVN,Git接触不久,感觉用着还行.写篇博文给大家分享一下使用Git的小经验,让大家对Git快速上手. 说白了Git就是一个控制版本的工具,其实没想象中的那么复杂,咱在 ...

随机推荐

  1. codeforces 361 C. Levko and Array Recovery(暴力+思维)

    题目链接:http://codeforces.com/contest/361/problem/C 题意:对一个数列有这么两个操作 1.(1,l,r,p)..将区间[l,r]所有数都加上p 2.(2,l ...

  2. codeforces 486 E. LIS of Sequence(dp)

    题目链接:http://codeforces.com/contest/486/problem/E 题意:给出n个数,如果一个数满足不属于最长递增序列,那么输出1,如果属于最长递增序列但是不属于所有最长 ...

  3. Python 之父的解析器系列之五:左递归 PEG 语法

    原题 | Left-recursive PEG grammars 作者 | Guido van Rossum(Python之父) 译者 | 豌豆花下猫("Python猫"公众号作者 ...

  4. poj 2352 & Ural 1028 数星星 题解

    一道水题,由于x坐标递增y坐标也递增于是前缀和统计即可,用树状数组实现. #include<bits/stdc++.h> using namespace std; const int ma ...

  5. 「每日五分钟,玩转JVM」:对象内存布局

    概览 一个对象根据不同情况可以被划分成两种情况,当对象是一个非数组对象的时候,对象头,实例数据,对齐填充在内存中三分天下,而数组对象中在对象头中多了一个用于描述数组对象长度的部分 对象头 对象头分为两 ...

  6. 【Offer】[24] 【反转链表】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点. 思路分析 利用三个指针,pre,p,pNext,将p的next ...

  7. Java中时间API使用详解

    目录 Java中时间API使用详解 1. 时区概念 2. 几种常见的时间 3. 时间戳 4. Java中的时间API 5. Java8中新添加的时间API 6. 在东八区的机器上获得美国时间 Java ...

  8. Ajax请求纯文本问题

    今天在学习Ajax时遇到一个问题: Ajax有个open(method String,url,boolean)方法,此方法有三个参数: 参数一:提交数据的请求,有GET和POST请求 GET:获取数据 ...

  9. 湘潭大学oj循环1-5

    #include <stdio.h>#include <stdlib.h> int main(){   int b,s,n;    int a[101]; A:scanf(&q ...

  10. Mysql使用SSL连接

    最近项目中用到了SSL连接,记录一下,环境为windows10,Mysql版本为5.6 查看是否支持 SSL 首先在 MySQL 上执行如下命令, 查询是否 MySQL 支持 SSL: mysql&g ...