• XXL-JOB逻辑自测及执行参数配置踩坑


    概述

    关于XXL-JOB的使用遇到的问题记录。对XXL-JOB不熟的,可以先参考分布式任务调度平台XXL-JOB深度实战

    实战

    业务DTO定义如下:

    @Data
    public class AdAccountDTO {
    	private String accountId;
    	/**
         * yyyy-MM-dd HH:mm:ss
         */
    	private String startCreateTime;
        private String endCreateTime;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    JobHandler定义如下:

    @Slf4j
    @Service
    @JobHander(value = "accountDependBindJobHandler")
    public class AccountDependBindJobHandler extends IJobHandler {
    
        @Resource
        private BaseService<AdAccountDTO, Void> accountDependBindService;
    
        @Override
        public ReturnT<String> execute(String... params) {
            try {
                log.info("accountDependBindJobHandler begin");
                AdAccountDTO account = new AdAccountDTO();
                account.setCreatorName("system");
                // 32行
                if (params.length > 0) {
                    account.setStartCreateTime(params[0]);
                }
                if (params.length > 1) {
                    // 广告主
                    account.setAccountId(params[1]);
                }
                if (params.length > 2) {
                    account.setEndCreateTime(params[2]);
                }
                log.info("accountDependBindJobHandler account: {}", JSONObject.toJSONString(account));
                accountDependBindService.execute(account);
                log.info("accountDependBindJobHandler end");
            } catch (Exception e) {
                log.error("accountDependBindJobHandler fail", e);
                return ReturnT.FAIL;
            }
            return ReturnT.SUCCESS;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    业务实现类AccountDependBindService,此处仅展示部分:

    @Override
    protected void business(BizContextVo<AdAccountDTO, Void> bizContextVo) {
        AdAccountDTO accountDTO = bizContextVo.getIn();
        String startDate = accountDTO.getStartCreateTime();
        String endDate = accountDTO.getEndCreateTime();
        boolean check = StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate) &&
                !DateUtils.isBefore(accountDTO.getStartCreateTime(), accountDTO.getEndCreateTime());
        if (check) {
            throw new BaseException("", "时间不匹配");
        }
        // 可以兼容startDate为null,"",及" "等情况
        check = StringUtils.isBlank(startDate);
        if (check) {
            // 默认跑数1天
            accountDTO.setStartCreateTime(DateUtils.formatDate(DateUtils.addDays(new Date(), -1), DateUtils.DATE_FORMAT_YMDHMS));
        }
        check = StringUtils.isBlank(endDate);
        if (check) {
            accountDTO.setEndCreateTime(DateUtils.getCurrentTime());
        }
        log.info("accountDependBindService account: {}", JSONObject.toJSONString(accountDTO));
        // 根据条件查询
        List<AdAccountDTO> accountList = advertiserMapper.selectAllNewAdvertiser(accountDTO);	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    XXL-JOB任务配置如下:
    在这里插入图片描述
    想要实现的效果是,如果执行参数为空,则取当前时间-24小时的数据开始同步。如果参数不为空,支持1-3个参数,第一个参数是同步开始时间,第二个是同步的账户,第三个是同步结束时间。

    问题

    自测

    因为代码的改动都是在本地,即开发环境。本地无法直接连接到测试环境的xxl-job系统,即在测试环境的xxl-job管理界面手动执行任务时,并不能把任务下发到我的localhost:8080调试环境,不能断点调试。也许xxl-job有这个功能,但是我并不知道。 参考下面的【本地调试】章节实现方案。

    想要验证job代码的逻辑,有以下3种方式:

    1. 代码提交到Git,fat环境打包部署,手动执行Job,查看日志,查看数据表,验证功能逻辑;
    2. 写一个Controller,Controller和JobHandler都是调用同一个Service业务实现类;
    3. 写一个UT,测试JobHandler的逻辑,同时还可以验证执行参数。

    方式1,效率最低,得反复修改代码,反复提交,反复部署;
    方式2,如果前端用不到Controller里新增的接口,或者后端开发人员也不需要借助于接口来补数据,或者同步数据,则会造成无用代码冗余;

    所以我们选择方式3。在\src\test\java目录下面新增一个UT测试类:

    @RunWith(SpringRunner.class)
    // 指定应用启动类
    @SpringBootTest(classes = WebApplication.class)
    public class JobHandlerTest {
        @Resource
        private AccountDependBindJobHandler accountDependBindJobHandler;
    
        @Test
        public void testAuto() {
        	// 模拟参数为空,null的情况
            // accountDependBindJobHandler.execute(null);
            // 模拟参数为空,empty的情况
            // String[] params = new String[]{};
            // 模拟2个参数的情况
            String[] params = new String[]{"2022-09-26", "34343434"};
            accountDependBindJobHandler.execute(params);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    本地调试

    XXL-JOB的执行器管理页如下:
    在这里插入图片描述
    注册方式可以是自动注册,也可以是手动注册。我们应用由于其特殊性,只能使用手动注册。参考分布式任务调度平台XXL-JOB深度实战执行器章节。

    我们不可能在本地搭建一套XXL-JOB管理平台,即:http://dev-xxljob.johnny.com/jobgroup。公司搭建有fat环境的一套平台:http://fat-xxljob.johnny.com/jobgroup。能不能通过fat-xxl-job来调度执行我们本地的代码,当然可以!

    编辑执行器,修改机器地址,指向我的本机IP:
    在这里插入图片描述
    然后在页面点击执行任务,任务详情如下:
    在这里插入图片描述
    然后请求确实打到本地,停在断点处:
    在这里插入图片描述
    这个截图也说明,如果执行参数为空时,params参数是null,而不是空字符串。

    注意:不能启动web应用!!!

    参考上面的文章,那篇文章提到,这里再说明下:web应用模块用于把任务注册同步到XXL-JOB平台,里面没有这个JobHandler类;dataset才是JobHandler所在的应用模块,这也是为什么不能使用自动注册方式。

    事实上,如果同时启动web和dataset两个应用模块。在XXL-JOB页面点击执行任务,会概率性出现调度失败的报错:
    在这里插入图片描述
    点击查看调度备注:
    在这里插入图片描述
    报错:找不到JobHandler。

    执行参数配置

    参考上面的UT类,模拟5种情况:

    1. 参数为null
    2. 参数为空字符串
    3. 1个参数
    4. 2个参数,多个参数直接使用英文逗号分隔
    5. 3个参数

    情况1自测下来会有报错:

    ERROR c.p.c.p.s.AccountDependBindJobHandler - accountDependBindJobHandler fail
    java.lang.NullPointerException: null
    	at com.johnny.cbd.platform.scheduled.AccountDependBindJobHandler.execute$original$Iu6X8Nnv(AccountDependBindJobHandler.java:32)
    	at com.johnny.cbd.platform.scheduled.AccountDependBindJobHandler.execute$original$Iu6X8Nnv$accessor$aikoAyrI(AccountDependBindJobHandler.java)
    	at com.johnny.cbd.platform.scheduled.AccountDependBindJobHandler$auxiliary$HcNstxGt.call(Unknown Source)
    	at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86)
    	at com.johnny.cbd.platform.scheduled.AccountDependBindJobHandler.execute(AccountDependBindJobHandler.java)
    	at com.johnny.job.core.thread.JobThread.run(JobThread.java:141)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    代码优化如下:

    if (params != null && params.length > 0) {
        account.setStartCreateTime(params[0]);
    }
    if (params != null && params.length > 1) {
        account.setAccountId(params[1]);
    }
    if (params != null && params.length > 2) {
        account.setEndCreateTime(params[2]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    XXL-JOB页面参数配置如果为空,就是上面的情况1,参考上面章节截图。

    XXL-JOB页面参数能实现参数配置为空字符串吗?答案是:不能!!
    在这里插入图片描述
    在这里插入图片描述
    当然改成双引号也不行!!!

    改成双引号后,应用打印的日志:

    INFO c.p.c.p.s.AccountDependBindJobHandler - accountDependBindJobHandler account: {"creatorName":"system","needPage":true,"offset":10,"pageSize":10,"start":0,"startCreateTime":"\"\""}
    
    • 1

    默认以当前时间为截至时间:

    INFO c.p.c.p.s.a.AccountDependBindServiceImpl - accountDependBindService account: {"creatorName":"system","endCreateTime":"2022-09-26 13:00:20","needPage":true,"offset":10,"pageIndex":1,"pageSize":10,"start":0,"startCreateTime":"\"\""} 
    
    • 1

    前面没有提到mapper.xml方法:

    <select id="selectAllNewAdvertiser" resultType="com.johnny.dto.AdAccountDTO">
        SELECT advertiser_id accountId, 4 as type FROM channel_advertiser_id WHERE isactive = 1
        <if test="status != null">
            and status = #{status}
        if>
        <if test="accountId != null">
            AND advertiser_id = #{accountId}
        if>
        <if test="startCreateTime != null">
            AND insert_time >= #{startCreateTime}
        if>
        <if test="endCreateTime != null">
            AND insert_time <= #{endCreateTime}
        if>
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    满足if条件:startCreateTime != null,拼接得到的SQL为:

    SELECT advertiser_id accountId, 4 as type FROM channel_advertiser_id WHERE isactive = 1 and advertiser_id = '1740858985811982' and insert_time >= '""'
    
    • 1

    有日志为证:

    2022-09-26 16:42:53,603 [main] DEBUG c.p.c.b.d.C.selectAllNewAdvertiser- ==>  Preparing: SELECT advertiser_id accountId, 4 as type FROM channel_advertiser_id WHERE isactive = 1 and status = ? AND advertiser_id = ? AND insert_time >= ? AND insert_time <= ? 
    2022-09-26 16:42:53,827 [main] DEBUG c.p.c.b.d.C.selectAllNewAdvertiser- ==> Parameters: 1(Integer), 1740858985811982(String), ""(String), 2022-09-26 16:42:49(String)
    2022-09-26 16:42:53,977 [main] DEBUG c.p.c.b.d.C.selectAllNewAdvertiser- <==      Total: 8
    
    • 1
    • 2
    • 3

    截图为证:
    在这里插入图片描述
    也就是说,我们在DataGrip上面看到虽然有Incorrect datetime value问题,但是MySQL会做隐式转换,还是可以查询出结果来。

    注,MySQL版本:5.7.29-32-log。

    另外3种参数配置case,没啥好说的。

  • 相关阅读:
    Java多线程(5):CAS
    ElasticJob 3.0.2 发布|优化 Failover、调度稳定性,支持 Java 19
    如何在快节奏的生活下摆脱焦虑?
    闭区间上连续函数的一些定理
    微擎小程序获取不到头像和昵称解决方案
    数据分析与挖掘———SPSS Moderler
    目前为止的所有取数逻辑收集
    【C/C++】结构体中使用变长数组问题分析
    pyspark分布式部署随机森林算法
    校园报修维修小程序,微信小程序报修系统,微信小程序宿舍报修系统毕设作品
  • 原文地址:https://blog.csdn.net/lonelymanontheway/article/details/127059620