首先我们可以先思考一下以下业务场景的解决方案:
这些业务场景的解决方案就是任务调度
任务调度是指系统为了自动完成特定任务,在约定的特定时刻去执行任务的过程。有了任务调度即可解放更多的人力,由系统自动去执行任务。
目前我们常见的分布式任务调度有:
在本章我们主要介绍的是Elastic-Job的使用。
Elastic-Job是一个分布式调度的解决方案,由当当网开源,它由两个相互独立的子项目Elastic-Job-Lite
和Elastic- Job-Cloud组成,使用Elastic-Job可以快速实现分布式任务调度。
在我们搭建环境,介绍Elastic-Job的使用之前我们需要介绍几个重要的概念:
1. 分片:
任务的分片执行,需要将一个任务分拆分为多个独立的任务然后由分布式的服务器分别执行某一个或几个分片项。
如果现在有一个遍历数据库某张表的任务,现有2台服务器,我们将这一个任务分成十片,那么每台服务器就要对应的执行五个分片任务,如果按照平均分配原则服务器A被分配到分片项0,1,2,3,4;服务器B被分配到分片项5,6,7,8,9。
2. leader选举:
zookeeper会保证在多台服务器中选举出一个leader,leader如果下线会触发重新选举。
3. 分片策略:
AverageAllocationJobShardingStrategy:
基于平均分配算法的分片策略,也是默认的分片策略。如果分片不能整除,则不能整除的多余分片将依次追加到序号小的服务器。
OdevitySortByNameJobShardingStrategy:
根据作业名的哈希值奇偶数决定IP升降序算法的分片策略。作业名的哈希值为奇数则IP升序。作业名的哈希值为偶数则IP降序。用于不同的作业平均分配负载至不同的服务器。
(注意:一旦分片数小于作业服务器数,作业将永远分配至IP地址靠前的服务器,导致IP地址靠后的服务器空闲。)
4. cron表达式:
cron表达式是一个字符串, 用来设置定时规则, 由七部分组成, 每部分中间用空格隔开;
组成部分 | 含义 | 取值范围 |
---|---|---|
第一部分 | Seconds (秒) | 0-59 |
第二部分 | Minutes(分) | 0-59 |
第三部分 | Hours(时) | 0-23 |
第四部分 | Day-of-Month(天) | 1-31 |
第五部分 | Month(月) | 0-11或JAN-DEC |
第六部分 | Day-of-Week(星期) | 1-7(1表示星期日)或SUN-SAT |
第七部分 | Year(年) 可选 | 1970-2099 |
cron表达式还有一些特殊符号
符号 | 含义 |
---|---|
? | 表示不确定的值。当两个子表达式其中一个被指定了值以后,为了避免冲突,需要将另外一个的值设为“?”。例如:想在每月20日触发调度,不管20号是星期几,只能用如下写法:0 0 0 20 * ?,其中最后以为只能用“?” |
* | 代表所有可能的值 |
, | 设置多个值,例如”26,29,33”表示在26分,29分和33分各自运行一次任务 |
- | 设置取值范围,例如”5-20”,表示从5分到20分钟每分钟运行一次任务 |
/ | 设置频率或间隔,如"1/15"表示从1分开始,每隔15分钟运行一次任务 |
L | 用于每月,或每周,表示每月的最后一天,或每个月的最后星期几,例如"6L"表示"每月的最后一个星期五" |
W | 表示离给定日期最近的工作日,例如"15W"放在每月(day-of-month)上表示"离本月15日最近的工作日" |
# | 表示该月第几个周X。例如”6#3”表示该月第3个周五 |
简单的举例说明,让我们有更加深刻的印象
cron表达式 | 含义 |
---|---|
*/5 * * * * ? | 每隔5秒运行一次任务 |
0 0 23 * * ? | 每天23点运行一次任务 |
0 0 1 1 * ? | 每月1号凌晨1点运行一次任务 |
0 0 23 L * ? | 每月最后一天23点运行一次任务 |
0 26,29,33 * * * ? | 在26分、29分、33分运行一次任务 |
0 0/30 9-17 * * ? | 朝九晚五工作时间内每半小时运行一次任务 |
0 15 10 ? * 6#3 | 每月的第三个星期五上午10:15运行一次任务 |
环境要求:
我们需要准备好数据库和对应的表,这里大家可以随意使用自己已经有的数据库和表来完成操作,我这里使用自己的数据库来演示
1、新建maven工程
2、引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.dangdanggroupId>
<artifactId>elastic-job-lite-springartifactId>
<version>2.1.5version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
dependency>
dependencies>
3、编写springBoot配置文件及启动类
server.port=${PORT:57081}
spring.application.name = elastic-job
logging.level.root = info
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.200.128:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
# 设置Mapper接口所对应的XML文件位置
mybatis-plus.mapper-locations = classpath*:dao/*.xml
# 设置别名包扫描路径
mybatis-plus.type-aliases-package = com.itxw.elasticjob.pojo
# zookeeper服务地址
zookeeper.connString = localhost:2181
# 名称空间
myjob.namespace = elastic-job-example
# 分片总数
myjob.count = 3
# cron表达式(定时策略)
myjob.cron = 0/5 * * * * ?
@MapperScan("com.itxw.elasticjob.dao")
@SpringBootApplication
public class ElasticJobApp {
public static void main(String[] args) {
SpringApplication.run(ElasticJobApp.class,args);
}
}
4、实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
@TableId("ID")
private Long id;
@TableField("USER_NAME")
private String userName;
@TableField("PASSWORD")
private String password;
@TableField("NAME")
private String name;
@TableField("AGE")
private Integer age;
}
5、数据访问层
public interface UserMapper extends BaseMapper<User> {
//对id通过mod函数取余进行分片
@Select("SELECT * FROM `tb_user` WHERE MOD(id,#{shardingTotalCount})=#{shardingItem}")
List<User> queryUserById(@Param("shardingTotalCount") int count, @Param("shardingItem") int item);
}
6、Elastic-Job任务类
/**
* 数据查询任务
**/
@Component
public class MyJob implements SimpleJob {
@Autowired
private UserMapper userMapper;
//执行定时任务
@Override
public void execute(ShardingContext shardingContext) {
//得到分片总数
int count = shardingContext.getShardingTotalCount();
//得到分片项
int item = shardingContext.getShardingItem();
//查询数据
List<User> userList = userMapper.queryUserById(count,item);
//输出结果
userList.forEach(user -> {
System.out.println("作业分片:" + item + "--->" + user);
});
}
}
7、zookeeper注册中心
@Configuration
public class ZKRegistryCenterConfig {
//zookeeper服务器地址
@Value("${zookeeper.connString}")
private String ZOOKEEPER_CONNECTION_STRING;
//定时任务的名称空间
@Value("${myjob.namespace}")
private String JOB_NAMESPACE;
//zk的配置及创建注册中心
@Bean(initMethod = "init")
public ZookeeperRegistryCenter setUpRegistryCenter() {
//zk配置
ZookeeperConfiguration zookeeperConfiguration = new
ZookeeperConfiguration(ZOOKEEPER_CONNECTION_STRING, JOB_NAMESPACE);
//创建注册中心
ZookeeperRegistryCenter zookeeperRegistryCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
return zookeeperRegistryCenter;
}
}
8、elastic-job配置类
@Configuration
public class ElasticJobConfig {
@Autowired
private MyJob myJob;
@Autowired
private ZookeeperRegistryCenter zkRegistryCenterConfig;
@Value("${myjob.count}")
private int shardingCount;
@Value("${myjob.cron}")
private String cron;
/**
* 配置任务详细信息
*
* @param jobClass 任务执行类
* @param cron 执行策略
* @param shardingTotalCount 分片数量
* @return
*/
private LiteJobConfiguration createJobConfiguration(final Class<? extends SimpleJob> jobClass,
final String cron,
final int shardingTotalCount) {
//创建JobCoreConfigurationBuilder
JobCoreConfiguration.Builder jobCoreConfigurationBuilder = JobCoreConfiguration.newBuilder(jobClass.getName(), cron, shardingTotalCount);
JobCoreConfiguration jobCoreConfiguration = jobCoreConfigurationBuilder.build();
//创建SimpleJobConfiguration
SimpleJobConfiguration simpleJobConfiguration = new SimpleJobConfiguration(jobCoreConfiguration, jobClass.getCanonicalName());
//创建LiteJobConfiguration
LiteJobConfiguration liteJobConfiguration = LiteJobConfiguration.newBuilder(simpleJobConfiguration).jobShardingStrategyClass("com.dangdang.ddframe.job.lite.api.strategy.impl.AverageAllocationJobShardingStrategy").overwrite(true).build();
return liteJobConfiguration;
}
@Bean(initMethod = "init")
public SpringJobScheduler initSimpleElasticJob() {
SpringJobScheduler springJobScheduler = new SpringJobScheduler(myJob, zkRegistryCenterConfig, createJobConfiguration(myJob.getClass(), cron, shardingCount));
return springJobScheduler;
}
}
以上代码完成后,我们就可以启动微服务去观察对应的数据了
我这里启用了三个服务,分配不同的端口号
等待一段时间后可以看到的效果如下: