从0搭建Java/Golang应用并接入

对于基于开源XXL-JOB自建任务调度系统的应用而言,可能会遇到任务配置复杂、任务执行效率低、监控和管理困难等需求。阿里云提供的开源解决方案,支持定时任务调度和任务分片等功能,帮助自建定时任务快速接入任务调度平台。

前提条件

方案概览

本方案将从零开始,使用Java语言,手把手教您搭建一个XXL-JOB定时任务,构建Docker镜像并上传至阿里云镜像仓库,然后将其接入到分布式任务调度平台XXL-JOB,进行单机和分片广播的定时任务测试,帮助您全面了解和掌握XXL-JOB的使用与配置。大致分为以下几步:

  1. 创建应用:通过创建应用对定时任务进行统一管理,方便查看、配置和调度,提升管理效率。

  2. 开发及部署应用:编写定时任务代码,构建Docker镜像,并将镜像上传至阿里云镜像仓库,实现应用的容器化管理和部署。

  3. 测试验证:确保已接入的应用能够在XXLJOB平台上正常自动化调度和管理,任务能够按计划准时准确执行。

步骤一:创建应用

  1. 登录XXL-JOB控制台,并在顶部菜单栏选择地域。

  2. 单击目标实例,进入实例详情页,在左侧导航栏,选择应用管理,单击创建应用。填写AppName名称,使用系统自动生成的AccessToken,单击确定

步骤二:开发及部署应用

1.开发XXL-JOB任务

说明

分布式任务调度XXL-JOB版支持Java应用和Go应用接入。如果您想了解更多信息,您可参考开源XXL-JOB的demo工程:

Java

  1. 环境配置:在您的pom.xml中引入xxl-job-core的maven依赖。具体版本,参考xxl-job-executor-sample-springboot

    <!-- xxl-job-core -->
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-job-core</artifactId>
        <version>2.2.x</version>
    </dependency>
  2. 初始化执行器。

    @Configuration
    public class XxlJobConfig {
        private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
    
        @Value("${xxl.job.admin.addresses}")
        private String adminAddresses;
    
        @Value("${xxl.job.accessToken}")
        private String accessToken;
    
        @Value("${xxl.job.executor.appname}")
        private String appname;
    
        @Value("${xxl.job.executor.address}")
        private String address;
    
        @Value("${xxl.job.executor.ip}")
        private String ip;
    
        @Value("${xxl.job.executor.port}")
        private int port;
    
        @Value("${xxl.job.executor.logpath}")
        private String logPath;
    
        @Value("${xxl.job.executor.logretentiondays}")
        private int logRetentionDays;
    
        @Bean
        public XxlJobSpringExecutor xxlJobExecutor() {
            logger.info(">>>>>>>>>>> xxl-job config init.");
            XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
            xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
            xxlJobSpringExecutor.setAppname(appname);
            xxlJobSpringExecutor.setAddress(address);
            xxlJobSpringExecutor.setIp(ip);
            xxlJobSpringExecutor.setPort(port);
            xxlJobSpringExecutor.setAccessToken(accessToken);
            xxlJobSpringExecutor.setLogPath(logPath);
            xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
    
            return xxlJobSpringExecutor;
        }
    
    }
  3. 编写任务执行代码,以2.2.x为例。

    说明

    XXLJOB不同版本接口不同,具体请参考开源demo工程。

    @Component
    public class SampleXxlJob {
        private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
    
        @XxlJob("helloworld")
        public ReturnT<String> helloworld(String param) throws Exception {
            XxlJobLogger.log("XXL-JOB, Hello World, start...");
            for (int i = 0; i < 5; i++) {
                XxlJobLogger.log("beat at:" + i);
                TimeUnit.SECONDS.sleep(2);
            }
            System.out.println("XXL-JOB, Hello World, finished");
            return ReturnT.SUCCESS;
        }
    }

Golang

  1. 环境配置:执行以下命令,使用最新的tag拉取Go版本的XXL-JOB的SDK。

    go get github.com/xxl-job/xxl-job-executor-go@{最新的tag}
  2. 编写任务执行代码。

    package main
    
    import (
        "context"
        "fmt"
        xxl "github.com/xxl-job/xxl-job-executor-go"
        "github.com/xxl-job/xxl-job-executor-go/example/task"
        "log"
    )
    
    func main() {
        exec := xxl.NewExecutor(
            xxl.ServerAddr("xxxxxx"),       //请求地址,控制台应用管理接入配置获取
            xxl.AccessToken("xxxxxxx"),     //请求令牌,控制台应用管理接入配置获取
            xxl.ExecutorPort("9999"),       //默认9999(非必填)
            xxl.RegistryKey("golang-jobs"), //执行器名称
            xxl.SetLogger(&logger{}),       //自定义日志
        )
        exec.Init()
        exec.Use(customMiddleware)
        //设置日志查看handler
        exec.LogHandler(customLogHandle)
        //注册任务handler
        exec.RegTask("task.test", task.Test)
        exec.RegTask("task.test2", task.Test2)
        exec.RegTask("task.panic", task.Panic)
        log.Fatal(exec.Run())
    }
    
    // 自定义日志处理器
    func customLogHandle(req *xxl.LogReq) *xxl.LogRes {
        return &xxl.LogRes{Code: xxl.SuccessCode, Msg: "", Content: xxl.LogResContent{
            FromLineNum: req.FromLineNum,
            ToLineNum:   2,
            LogContent:  "这个是自定义日志handler",
            IsEnd:       true,
        }}
    }
    
    // xxl.Logger接口实现
    type logger struct{}
    
    func (l *logger) Info(format string, a ...interface{}) {
        fmt.Println(fmt.Sprintf("自定义日志 - "+format, a...))
    }
    
    func (l *logger) Error(format string, a ...interface{}) {
        log.Println(fmt.Sprintf("自定义日志 - "+format, a...))
    }
    
    // 自定义中间件
    func customMiddleware(tf xxl.TaskFunc) xxl.TaskFunc {
        return func(cxt context.Context, param *xxl.RunReq) string {
            log.Println("I am a middleware start")
            res := tf(cxt, param)
            log.Println("I am a middleware end")
            return res
        }
    }
    

2.部署应用至阿里云

阿里云XXL-JOB版仅支持阿里云网络,需要将您的应用部署到阿里云上,以下以Java应用部署到容器服务为例进行说明。

重要

容器服务集群需要和任务调度XXLJOB集群在同一个VPC内。

  1. 在SpringBoot应用的根目录下编写Dockerfile文件。

    # 下面替换你自己的基础镜像
    FROM reg.docker.alibaba-inc.com/xxx/xxxx-java:1.0-beta
    MAINTAINER xueren
    ENV JAVA_OPTS=""
    ADD target/xxl-job-executor-sample-springboot-*.jar /app.jar
    ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app.jar]
  2. 使用Docker工具,构建Docker镜像,并上传到阿里云镜像仓库

    docker login --username=xxx@aliyun.com registry.cn-hangzhou.aliyuncs.com --password=xxxxxx
    docker buildx build --platform linux/amd64 -t registry.cn-hangzhou.aliyuncs.com/schedulerx/xxljob-demo:2.2.0 .
    docker push registry.cn-hangzhou.aliyuncs.com/schedulerx/xxljob-demo:2.2.0
  3. 在左侧导航栏的应用管理页面,单击目标应用操作列的接入配置

  4. 登录阿里云容器服务,进入目标集群,单击使用Yaml创建资源,创建Deployment。以接入方式2(通过-D参数重启应用)的为例进行接入配置,替换YAML的JAVA_OPTS列中,实现环境变量注入JVM参数。

    image

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: xxljob-xueren-test
      labels:
        app: xxljob-xueren-test
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: xxljob-xueren-test
      template:
        metadata:
          labels:
            app: xxljob-xueren-test
        spec:
          containers:
          - name: xxljob-executor
            image: registry.cn-hangzhou.aliyuncs.com/schedulerx/xxljob-demo:2.2.0
            ports:
            - containerPort: 9999
            env:
              - name: JAVA_OPTS
                value: >-
                  -Dxxl.job.admin.addresses=http://xxljob-xxxxx.schedulerx.mse.aliyuncs.com
                  -Dxxl.job.executor.appname=xueren_test
                  -Dxxl.job.accessToken=xxxxxxx

步骤三:测试验证

1.执行器接入验证

进入目标实例详情页,单击左侧导航栏的应用管理,在应用列表页面,单击目标应用的执行器数量,可看到接入的执行器地址和在线状态。

image

2.任务测试验证

单机任务测试

单机任务表示每次执行在该应用下的所有执行器中按照路由策略选一台幂等执行。

  1. 在左侧导航栏,选择任务管理,单击创建任务。首先进行基本配置,填写任务名称jobHandler名称关联应用选择目标应用,路由策略选择轮询,然后单击下一步

    image

  2. 进行定时配置时间类型选择cron,通过单击使用生成工具按钮,生成cron表达式。本示例以每天12时执行一次为例,然后单击下一步

    image

  3. 进行通知配置,您可以在此配置超时报警、成功通知、失败报警、通知方式、通知对象。本示例以控制台默认配置为例。

    image

  4. 创建完成后,单击目标任务操作列中的运行一次,在手动执行任务弹框中,指定机器,配置实例参数,单击确定

    image

  5. 单击更多 > 调度记录,查看任务执行记录。

    image

  6. 单击左侧导航栏执行列表,单击目标执行记录操作列中的日志,查看本次任务的执行日志记录。

    image

分片广播任务测试

广播分片表示每次执行会广播该应用下所有执行器执行,每个执行器能拿到不同的分片号,可以用来做分布式批处理。开源XXLJOB分片广播无聚合功能,阿里云XXLJOB可以聚合展示每次执行的所有分片情况。

  1. 在左侧导航栏,选择任务管理,单击创建任务。首先进行基本配置,填写任务名称jobHandler名称关联应用选择目标应用,路由策略选择分片广播。然后单击下一步

    image

  2. 进行定时配置时间类型选择cron,通过单击使用生成工具按钮,生成cron表达式。本示例以每小时第10分执行一次为例,然后单击下一步

    image

  3. 进行通知配置,您可以在此配置设置超时报警、成功通知、失败报警、通知方式、通知对象。本示例以控制台默认配置为例。

    image

  4. 创建完成后,单击目标任务操作列中的运行一次,在手动执行任务弹框中,指定机器,配置实例参数,单击确定

    image

  5. 单击更多 > 调度记录,查看任务执行记录。

    image

  6. 单击左侧导航栏的执行列表,在任务执行列表页,单击目标任务执行操作列中的详情,在分片详情中可聚合展示每台机器的执行情况。

    image

  7. 针对每个分片,单击指定分片操作列中的日志,查看本次任务的执行日志记录。

    image