1.  ElasticJob 是什么

ElasticJob 是一个分布式调度解决方案,由两个相互独立的子项目 ElasticJob-Lite 和 ElasticJob-Cloud 组成。

ElasticJob-Lite 定位为轻量级无中心化解决方案,使用jar的形式提供分布式任务的协调服务。

ElasticJob 已于2020年5月28日成为 Apache ShardingSphere 的子项目。

ElasticJob特性:

  • 弹性调度

    • 支持任务在分布式场景下的分片和高可用
    • 能够水平扩展任务的吞吐量和执行效率
    • 任务处理能力随资源配备弹性伸缩 
  • 资源分配
    • 在适合的时间将适合的资源分配给任务并使其生效
    • 相同任务聚合至相同的执行器统一处理
    • 动态调配追加资源至新分配的任务  
  • 作业治理
    • 失效转移
    • 错过作业重新执行
    • 自诊断修复
  • 作业开放生态
    • 可扩展的作业类型统一接口
    • 丰富的作业类型库,如数据流、脚本、HTTP、文件、大数据等
    • 易于对接业务作业,能够与 Spring 依赖注入无缝整合  
  • 可视化管控端
    • 作业管控端
    • 作业执行历史数据追踪
    • 注册中心管理 

2.  实例演示

这里采用最新版本 3.0.0-RC1

1、启动zookeeper服务

首先,下载zookeeper-3.6.0版本,解压后复制一份zoo_sample.cfg,重命名未zoo.cfg,保持默认配置即可

注意,zookeeper-3.6.0启动以后会占用三个端口,其中包括8080哦

2、编写定时任务业务逻辑

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>elasticjob-demo</artifactId>
<version>0.0.1-SNAPSHOT</version> <properties>
<java.version>1.8</java.version>
<elasticjob-lite.version>3.0.0-RC1</elasticjob-lite.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.apache.shardingsphere.elasticjob</groupId>
<artifactId>elasticjob-lite-spring-boot-starter</artifactId>
<version>${elasticjob-lite.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>org.apache.shardingsphere.elasticjob</groupId>
<artifactId>elasticjob-error-handler-dingtalk</artifactId>
<version>${elasticjob-lite.version}</version>
</dependency>

</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

application.yml

elasticjob:
regCenter:
serverLists: 192.168.100.15:2181
namespace: elasticjob-demo
baseSleepTimeMilliseconds: 2000
maxSleepTimeMilliseconds: 4000
maxRetries: 3
jobs:
firstJob:
elasticJobClass: com.example.job.FirstJob
cron: 0/6 * * * * ?
shardingTotalCount: 3
jobErrorHandlerType: DINGTALK
props:
dingtalk:
webhook: https://oapi.dingtalk.com/robot/send?access_token=xxx
secret: ASDF
connectTimeout: 3000
readTimeout: 5000
secondJob:
elasticJobClass: com.example.job.SecondJob
cron: 0/10 * * * * ?
shardingTotalCount: 1
jobErrorHandlerType: DINGTALK
props:
dingtalk:
webhook: https://oapi.dingtalk.com/robot/send?access_token=xxx
secret: ASDF
connectTimeout: 3000
readTimeout: 5000 

两个定时任务

FirstJob.java

package com.example.job;

import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.simple.job.SimpleJob;
import org.springframework.stereotype.Component; /**
* @author ChengJianSheng
* @date 2021/1/13
*/
@Component
public class FirstJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
switch (shardingContext.getShardingItem()) {
case 0:
// do something by sharding item 0
System.out.println(0);
// int a = 1 / 0;
break;
case 1:
// do something by sharding item 1
System.out.println(1);
break;
case 2:
// do something by sharding item 2
System.out.println(2);
break;
// case n: ...
}
}
}

SecondJob.java

package com.example.job;

import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.simple.job.SimpleJob;
import org.springframework.stereotype.Component; /**
* @author ChengJianSheng
* @date 2021/1/18
*/
@Component
public class SecondJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
System.out.println("hello");
}

项目结构

运行项目即可

通过 ElasticJob-UI 查看任务

https://shardingsphere.apache.org/elasticjob/current/cn/downloads/

3.  启动报错排查

项目启动过程中,可能会报如下错误

org.apache.zookeeper.ClientCnxn$EndOfStreamException: Unable to read additional data from server sessionid 0x1000bdf48160002, likely server has closed socket

org.apache.shardingsphere.elasticjob.reg.exception.RegException: org.apache.zookeeper.KeeperException$OperationTimeoutException: KeeperErrorCode = OperationTimeout

Caused by: org.apache.zookeeper.KeeperException$OperationTimeoutException: KeeperErrorCode = OperationTimeout

最开始,我以为是zookeeper版本的问题,后来换了版本也不行,防火墙关了也不行

然后,我怀疑是开发环境问题,于是在本地运行zookeeper,程序连127.0.0.1:2181,居然可以了

于是我陷入了沉思,为今之计,只剩下一个办法了,打断点调试

找到了异常抛出的位置,如下图

baseSleepTimeMilliseconds 表示 等待重试的间隔时间的初始值

maxSleepTimeMilliseconds  表示 等待重试的间隔时间的最大值

maxRetries 表示 最大重试次数

根据代码中意思,如果在 maxSleepTimeMilliseconds * maxRetries 毫秒内还没有连接成功,则连接关闭,并抛出操作超时异常

联想到,连接本地zookeeper可以,连开发环境zk就不行,再加上观察日志从连接开始到抛异常的时间间隔,我猜到应该是maxSleepTimeMilliseconds设置太短了

于是,application.yml配置文件中将maxSleepTimeMilliseconds设置为4000,baseSleepTimeMilliseconds设置为2000

然后好使

回想刚开始报的那些错,其实根本就还没有连上zookeeper

4.  作业分片

ElasticJob 中任务分片项的概念,使得任务可以在分布式的环境下运行,每台任务服务器只运行分配给该服务器的分片。 随着服务器的增加或宕机,ElasticJob 会近乎实时的感知服务器数量的变更,从而重新为分布式的任务服务器分配更加合理的任务分片项,使得任务可以随着资源的增加而提升效率。

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

也就是说,分片是为了在分布式环境下高效合理利用任务服务器资源的。简单地来讲,一个定时任务,我们运行多台服务器,这意味着有多个实例在执行同一项任务,分片就是为了告诉这些实例各自该处理那些数据,最大限度的降低数据重复处理的问题,同时加快任务处理速度。每个任务实例该处理哪些数据,是根据分片项来的,在任务代码层面,就可以根据分片项来进行逻辑判断。

举例说明,如果作业分为 4 片,用两台服务器执行,则每个服务器分到 2 片,分别负责作业的 50% 的负载

分片项

ElasticJob 并不直接提供数据处理的功能,而是将分片项分配至各个运行中的作业服务器,开发者需要自行处理分片项与业务的对应关系。 分片项为数字,始于 0 而终于分片总数减 1。

个性化分片参数

个性化参数可以和分片项匹配对应关系,用于将分片项的数字转换为更加可读的业务代码。

合理使用个性化参数可以让代码更可读。例如,如果配置为 0=北京,1=上海,2=广州,那么代码中直接使用北京,上海,广州的枚举值即可完成分片项和业务逻辑的对应关系。

分片策略

平均分片策略

根据分片项平均分片。如果作业服务器数量与分片总数无法整除,多余的分片将会顺序的分配至每一个作业服务器。

举例说明:

  • 如果 3 台作业服务器且分片总数为9, 则分片结果为:1=[0,1,2], 2=[3,4,5], 3=[6,7,8]
  • 如果 3 台作业服务器且分片总数为8, 则分片结果为:1=[0,1,6], 2=[2,3,7], 3=[4,5]
  • 如果 3 台作业服务器且分片总数为10,则分片结果为:1=[0,1,2,9], 2=[3,4,5], 3=[6,7,8]

奇偶分片策略 

根据作业名称哈希值的奇偶数决定按照作业服务器 IP 升序或是降序的方式分片。

如果作业名称哈希值是偶数,则按照 IP 地址进行升序分片; 如果作业名称哈希值是奇数,则按照 IP 地址进行降序分片。 可用于让服务器负载在多个作业共同运行时分配的更加均匀。

举例说明:

  • 如果 3 台作业服务器,分片总数为2且作业名称的哈希值为偶数,则分片结果为:1 = [0], 2 = [1], 3 = []
  • 如果 3 台作业服务器,分片总数为2且作业名称的哈希值为奇数,则分片结果为:3 = [0], 2 = [1], 1 = []

轮询分片策略

根据作业名称轮询分片。

5.  官方文档

https://shardingsphere.apache.org/elasticjob/current/cn/features/elastic/

https://shardingsphere.apache.org/elasticjob/current/cn/user-manual/elasticjob-lite/

https://shardingsphere.apache.org/elasticjob/current/cn/user-manual/elasticjob-lite/configuration/

https://shardingsphere.apache.org/elasticjob/current/cn/dev-manual/

ElasticJob 快速上手的更多相关文章

  1. 分布式作业 Elastic-Job 快速上手指南,从理论到实战一文搞定!

    Elastic-Job支持 JAVA API 和 Spring 配置两种方式配置任务,这里我们使用 JAVA API 的形式来创建一个简单的任务入门,现在都是 Spring Boot 时代了,所以不建 ...

  2. 【Python五篇慢慢弹】快速上手学python

    快速上手学python 作者:白宁超 2016年10月4日19:59:39 摘要:python语言俨然不算新技术,七八年前甚至更早已有很多人研习,只是没有现在流行罢了.之所以当下如此盛行,我想肯定是多 ...

  3. 快速上手Unity原生Json库

    现在新版的Unity(印象中是从5.3开始)已经提供了原生的Json库,以前一直使用LitJson,研究了一下Unity用的JsonUtility工具类的使用,发现使用还挺方便的,所以打算把项目中的J ...

  4. [译]:Xamarin.Android开发入门——Hello,Android Multiscreen快速上手

    原文链接:Hello, Android Multiscreen Quickstart. 译文链接:Hello,Android Multiscreen快速上手 本部分介绍利用Xamarin.Androi ...

  5. [译]:Xamarin.Android开发入门——Hello,Android快速上手

    返回索引目录 原文链接:Hello, Android_Quickstart. 译文链接:Xamarin.Android开发入门--Hello,Android快速上手 本部分介绍利用Xamarin开发A ...

  6. 快速上手seajs——简单易用Seajs

    快速上手seajs——简单易用Seajs   原文  http://www.cnblogs.com/xjchenhao/p/4021775.html 主题 SeaJS 简易手册 http://yslo ...

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

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

  8. Objective-C快速上手

    最近在开发iOS程序,这篇博文的内容是刚学习Objective-C时做的笔记,力图达到用最短的时间了解OC并使用OC.Objective-C是OS X 和 iOS平台上面的主要编程语言,它是C语言的超 ...

  9. Netron开发快速上手(二):Netron序列化

    Netron是一个C#开源图形库,可以帮助开发人员开发出类似Visio的作图软件.本文继前文”Netron开发快速上手(一)“讨论如何利用Netron里的序列化功能快速保存自己开发的图形对象. 一个用 ...

随机推荐

  1. 微信小程序日期转换、比较、加减

    直接上干货: 在utils目录下新建一个dateUtil.js,代码如下:(在需要用的地方引入这个js,调用相关方法传入对应参数就可以使用了) 该工具脚本,实用性很高,通用于各类前端项目,熟悉后亦可以 ...

  2. I/O方式(本章最重要)

    目录 程序查询方式 程序查询方式接口结构 例题 本节回顾 程序中断方式 中断的基本概念 工作流程 中断请求 分类 中断请求标记 中断响应 判优实现 优先级设置 中断处理过程 中断隐指令 硬件向量法 中 ...

  3. tp5使用PHPExcel(下载引入/composer与模板/生成方式搭配使用)

    PHPExcel使用 一:引入 tp5.0,tp5.1: 1:composer方式 a:根目录下执行:composer require phpoffice/phpexcel b:引入(可new \PH ...

  4. 全能扫描王(一款识别率超高的OCR识别APP)

    前言 无论是在工作还是日常生活中,我们都会经常遇到,需要将一些纸质资料上的文字内容变成电子文档进行编辑.这个时候就需要拥有一款好用的手机扫描+OCR文字识别功能的应用了. 随着人工智能的兴起,我们都在 ...

  5. 嵌入式开发笔记——调试组件SEGGER_RTT

    一.前言 在嵌入式开发过程中,经常会通过打印输出一些调试信息来调试参数.查找问题等,通常我的做法都是使用芯片的串口硬件设备配合串口助手软件来进行调试.但是这次项目的PCB硬件设计并未预留串口调试接口, ...

  6. CVE-2019-0708_RDP漏洞利用

    可以说是2019年影响比较大的一个漏洞了, 简述下这个漏洞: Windows系列服务器于2019年5月15号,被爆出高危漏洞,该漏洞影响范围较广如: windows2003.windows2008.w ...

  7. 你只用do-while来实现循环?太浪费了!

    这是道哥的第010篇原创 目录 前言 在宏定义中的妙用 错误的宏定义 比较好的宏定义 另一个也不错的宏定义 在函数体中的妙用 函数功能:返回错误代码对应的错误字符串 函数功能:通过TCP Socket ...

  8. H3C路由器配置——动态路由RIP协议

    一.静态路由的不足 静态路由适用于:小规模的网络.架构不怎么调整的网络.没有环路的网络 二.RIP协议工作过程 2.1.工作特点 n路由信息协议RIP(Routing Information Prot ...

  9. vue-style-loader源码初步分析

    背景: 首先声明一下,我只是个菜鸡,为了解决问题才去看的源码,解决完问题之后也就没有兴趣看其他部分代码了,所以这篇文章是一次很低层次的解读,角度也相当片面,想必会有很多喷点吧. 事情的经过是这样,今年 ...

  10. Atlas 2.1.0 实践(2)—— 安装Atlas

    在完成Atlas编译以后,就可以进行Atlas的安装了.Atlas的安装主要是安装Atlas的Server端,也就Atlas的管理页面,并确保Atlas与Kafka Hbase Solr等组件的集成. ...