elastic-job 分布式任务调度实例demo,springboot+elastic-job

什么时候用到分布式任务调度

任务调度就是定时任务。分布式任务调度区别于单节点任务调度,分布式任务调度可以让集群环境下的各个节点执行不同的任务,以此提高效率。

什么场景用到任务调度呢?

例如1.借贷App,逾期问题,自动发送短信,自动计算更新逾期金额;

       2.电商给满足条件的用户自动发送优惠券;

       3.互联网项目缓存预热,在用户查看之前提前准备好缓存;

基本概念

  • 分片概念

将一个大任务拆分成多个小任务,在各个节点上执行。

  • 分片项与业务处理解耦

elastic-job不提供数据处理的功能,框架只会将分片项分配至各个运行中的作业服务器,开发者需要自行处理分片项与真实数据的对应关系。

  • 个性化参数的适用场景

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

例如:按照水平拆分数据库,数据库A是北京的数据;数据库B是上海的数据;数据库C是广州的数据。按照分片项配置,开发者需要了解0表示北京;1表示上海;2表示广州。合理适用个性化参数可以让代码更可读,如果配置0=北京,1=上海,2=广州,那么代码中直接适用北京、上海、广州的枚举即可完成分片项和业务逻辑的对应关系。

  • 分布式调度

Elastic-Job-Lite 并无作业调度中心节点,而是基于部署做业框架的程序到达相应时间点时各自触发调度。注册中心仅用于作业注册和监控信息存储。而主作业节点仅用于处理分片和清理等功能

  • 作业高可用

Elastic-Job-Lite 提供最安全的方式执行作业。将分片总数设置为1,并使用多余1台的服务器执行作业,作业将会以1主n从的方式执行。一旦执行作业的服务器崩溃,等待执行的服务器将会在下次作业启动时替补执行。开启失效转移功能效果更好,可以保证在本次作业执行时崩溃,备机立即启动替补执行。

  • 最大限度利用资源

Elastic-Job-Lite 也提供最灵活的方式,最大限度的提高执行作业的吞吐量。将分片项设置为大于服务器的数量,最好是大于服务器倍数的数量,作业将会合理的利用分布式资源,动态的分配分片项。例如:3台服务器,分成10片,则分片项分配结果为服务器A=0,1,2;服务器B=3,4,5;服务器C=6,7,8,9.如果服务器C崩溃,则分片项分配结果为服务器A=0,1,2,3,4;服务器B=5,6,7,8,9.在不丢失分片项的情况下,最大限度的利用现有资源提高吞吐量。

认识 Elastic-Job

Elastic-Job是一个分布式任务调度框架,由当当网基于Quartz开发,于2020 年 5 月 28 日开源给apache。使用 Zookeeper  作为注册中心,支持弹性扩容和数据分片。
Elastic-Job有Elastic-Job-Lite 和 Elastic-Job-Cloud 两个子项目。Elastic-Job-Lite 定位为轻量级无中心化解决方案, 使用jar包的形式提供分布式任务的协调服务 。而Elastic-Job-Cloud 使用 Mesos + Docker 的解决方案,额外提供资源治理、应用分发以及进程隔离等服务(跟 Lite 的区别只是部署方式不同,他们使用相同的 API,只要开发一次)。本文只介绍Elastic-Job-Lite的demo。Elastic-Job支持3种作业类型,分别是SimpleJob、DataFlowJob、ScriptJob,此篇使用的是SimpleJob。

实例

注意elastic-job依赖zookeeper,首先要安装好zookeeper。

接下来我们模仿现实中的一个场景:电商收到订单后,需要按地址分发物流。假设有北京、上海、深圳、广州四个地址,为了提高吞吐量,采用4台机器4个节点进行分片处理。

application.properties:

zk.serverList=118.25.53.252:2181
zk.namespace=elasticjob-test

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.3.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>pers.czl</groupId>
	<artifactId>elasticJobDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>elasticJobDemo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.dangdang</groupId>
			<artifactId>elastic-job-lite-core</artifactId>
			<version>2.1.5</version>
		</dependency>
		<!-- elastic-job-lite-spring -->
		<dependency>
			<groupId>com.dangdang</groupId>
			<artifactId>elastic-job-lite-spring</artifactId>
			<version>2.1.5</version>
		</dependency>


	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<target>1.8</target>
					<source>1.8</source>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

注册中心配置类

package pers.czl.elasticjob.config;

import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperConfiguration;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Title: 注册中心配置类
 * @Description:
 * @Author:
 * @CreateTime: 2020-12-17 16:53
 * @Version:1.0
 **/
@Configuration
public class RegCenterConfig {
    /**
     * 配置zookeeper
     * @param serverList
     * @param namespace
     * @return
     */
    @Bean(initMethod = "init")
    public ZookeeperRegistryCenter zookeeperRegistryCenter(
            @Value("${zk.serverList}") final String serverList,
            @Value("${zk.namespace}") final String namespace) {
        return new ZookeeperRegistryCenter(new ZookeeperConfiguration(serverList, namespace));
    }
}

任务配置类

"0/5 * * * * ?"是cron表达式,代表每5秒钟执行一次

package pers.czl.elasticjob.config;

import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.dangdang.ddframe.job.config.JobCoreConfiguration;
import com.dangdang.ddframe.job.config.JobRootConfiguration;
import com.dangdang.ddframe.job.config.simple.SimpleJobConfiguration;
import com.dangdang.ddframe.job.lite.api.JobScheduler;
import com.dangdang.ddframe.job.lite.config.LiteJobConfiguration;
import com.dangdang.ddframe.job.lite.spring.api.SpringJobScheduler;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import pers.czl.elasticjob.job.MySimpleJob;

/**
 * @Title: 作业配置类
 * @Description:
 * @Author: wb-ccl670938
 * @CreateTime: 2020-12-17 17:18
 * @Version:1.0
 **/
@Configuration
public class MySimpleJobConfig {

    @Autowired
    private ZookeeperRegistryCenter zookeeperRegistryCenter;

    @Bean
    public SimpleJob stockJob() {
        return new MySimpleJob();
    }

    @Bean(initMethod = "init")
    public JobScheduler simpleJobScheduler(final SimpleJob simpleJob) {
        return new SpringJobScheduler(simpleJob, zookeeperRegistryCenter, createSimpleJobConfiguration());
    }

    private static LiteJobConfiguration createSimpleJobConfiguration() {
        // 定义作业核心配置
        JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder("SimpleJobDemo", "0/5 * * * * ?", 4)
                .shardingItemParameters("0=北京,1=上海,2=广州,3=深圳").build();
        // 定义SIMPLE类型配置
        SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, MySimpleJob.class.getCanonicalName());
        // 定义Lite作业根配置
        JobRootConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).build();

        return (LiteJobConfiguration) simpleJobRootConfig;

    }
}

作业类

package pers.czl.elasticjob.job;

import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;

/**
 * @Title: 作业类
 * @Description:
 * @Author: wb-ccl670938
 * @CreateTime: 2020-12-17 16:44
 * @Version:1.0
 **/
public class MySimpleJob implements SimpleJob {

    @Override
    public void execute(ShardingContext shardingContext) {
        //获取分片总数
        int shardingTotalCount = shardingContext.getShardingTotalCount();
        //获取分片项
        int shardingItem = shardingContext.getShardingItem();
        //获取分片项参数
        String shardingParameter = shardingContext.getShardingParameter();

        System.out.println("分片总数:" + shardingTotalCount);
        System.out.println("分片项:" + shardingItem);
        System.out.println("分片项参数:" + shardingParameter);
        System.out.println("作业名称:" + shardingContext.getJobName());

        //不同分片项进行不同处理
        switch (shardingItem) {
            case 0:
                System.out.println("物流分发至:" + shardingParameter);
                break;
            case 1:
                System.out.println("物流分发至:" + shardingParameter);
                break;
            case 2:
                System.out.println("物流分发至:" + shardingParameter);
                break;
            case 3:
                System.out.println("物流分发至:" + shardingParameter);
                break;
        }

    }
}

启动类

package pers.czl.elasticjob;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ElasticJobDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(ElasticJobDemoApplication.class, args);
	}

}

代码完成,首先启动一个实例,看输出结果

可以看到所有分片项都被分发到这一个实例。那么继续启动3个实例。

可以看到4个分片项被均匀的分配给了4个节点。

现在停掉一个实例

停掉分片项3的实例后,分片项3和0被分配给了第二个实例。这样就实现了故障的转移。

注册中心节点简介

项目启动后会在zookeeper注册中心生成节点。

config:job配置信息,持久化节点

{
    "jobName":"SimpleJobDemo",
    "jobClass":"pers.czl.elasticjob.job.MySimpleJob",
    "jobType":"SIMPLE",
    "cron":"0/5 * * * * ?",
    "shardingTotalCount":4,
    "shardingItemParameters":"0=鍖椾含,1=涓婃捣,2=骞垮窞,3=娣卞湷",
    "jobParameter":"",
    "failover":false,
    "misfire":true,
    "description":"",
    "jobProperties":{
        "job_exception_handler":"com.dangdang.ddframe.job.executor.handler.impl.DefaultJobExceptionHandler",
        "executor_service_handler":"com.dangdang.ddframe.job.executor.handler.impl.DefaultExecutorServiceHandler"
    },
    "monitorExecution":true,
    "maxTimeDiffSeconds":-1,
    "monitorPort":-1,
    "jobShardingStrategyClass":"",
    "reconcileIntervalMinutes":10,
    "disabled":false,
    "overwrite":false
}

instances:启动的实例。本例先启动了4个,停止了一个剩下3个。由ip+@-@+pid组成。

sharding:分片信息,子节点是分片序号。记录的数据是当前分片分配给的实例。

servers:任务实例的ip信息。

leader:实例的主节点,通过zookeeper的主节点选举选选出来的。主节点负责控制分片,把分片分配给不同的实例。有三个子节点:
             1.election:主节点选举 。instance记录的是选举出来的主节点,此例的主节点pid是7032。election 下面的 latch 节点也是一个永久节点用于选举时候的实现分布式锁
             2.sharding:分片
             3.failover:失效转移,没有发生失效转移所以没显示

 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值