如何在项目中集成XXL-JOB

选择xxl-job的原因

首先就是这是一个分布式项目,所以通过直接本地定时任务或是springboot级别的定时任务就先排除掉了,因为这种都是本地级别的,而且是分散在各个项目里面的,不好做统一的管理和观察,如果修改cron表达式需要重启项目

那如果是Quartz呢?需要自研集群机制和手写广播分片机制,并且现在社区活跃度低

然后像Elastic-Job、PowerJob又需要引入额外的中间件

我们最终的目标是能实现定时任务的执行操作和业务分隔开来,也就是能线上随时调整定时任务的执行频率,并且能有相应的数据报表来统一的查看任务的执行情况,统一进行管理,也不想引入额外的中间件。

所以xxl-job刚好就符合这个情况,因为只需要使用到mysql,没有额外的中间件。

如何集成到项目中

首先xxl-job就是一个单独的服务

admin模块就是对外暴露的接口,我们就是通过访问这个网页和xljob进行交互的

core就是底层实现定时任务的核心逻辑,如果是有对这部分的代码进行修改的话,可以直接使用自己的那个坐标,否则直接使用官方推荐的就行

复制代码
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.4.0</version>
</dependency>

最后那个就是一个执行器示例

注意:使用之前需要先去执行一下对应的sql脚本,把对应的表都先创建好

xxljob主要就是两部分组成的,一部分就是admin服务,一部分就是执行器(也就是我们的业务代码)

我们只需要启动admin,然后在我们的业务代码中引入core依赖(引入了这个就算是执行器),并编写配置文件和配置类就行了

配置类:

复制代码
@Configuration
@EnableConfigurationProperties(XxlJobProperties.class)
@ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true", matchIfMissing = true)
public class XxlJobAutoConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(XxlJobAutoConfiguration.class);

    @Bean
    public XxlJobSpringExecutor xxlJobSpringExecutor(XxlJobProperties props) {
        LOGGER.info("已经启动XxlJob配置类");
        XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
        executor.setAdminAddresses(props.getAdmin().getAddresses());
        executor.setAppname(props.getExecutor().getAppname());
        executor.setIp(props.getExecutor().getIp());
        executor.setPort(props.getExecutor().getPort());
        executor.setLogPath(props.getExecutor().getLogPath());
        executor.setLogRetentionDays(props.getExecutor().getLogRetentionDays());
        executor.setAccessToken(props.getAccessToken());
        return executor;
    }
}

@Data
@ConfigurationProperties(prefix = "xxl.job")
public class XxlJobProperties {
    private String accessToken;
    private Admin admin = new Admin();
    private Executor executor = new Executor();

    @Data
    public static class Admin {
        private String addresses;
    }

    @Data
    public static class Executor {
        private String appname;
        private String ip;
        private Integer port = 9999;
        private String logPath = "./logs/xxl-job";
        private Integer logRetentionDays = 7;
    }
}

配置文件:

复制代码
xxl:
  job:
    admin:
      addresses: http://localhost:10000/xxl-job-admin
    accessToken:
    executor:
      appname: livingjobHandler
      address:
      ip:
      port: 9999
      logpath: ./logs/xxl-job
      logretentiondays: 7

Tips:这里我是修改了启动的端口为10000,如果你是直接使用默认的话应该是8080

然后要注意的就是配置文件里面会写一个port参数,这个port参数就是执行器监听端口,因为core模块底层是内置了一个netty服务的,所以需要专门暴露一个端口出来和admin模块进行通信,这个端口随便写就行了,只要是自己的电脑上没被占用的就行,admin会自己找到这个端口的

然后做完这一切之后就是到http://localhost:10000/xxl-job-admin/,也就是页面上进行操作

首先需要创建出执行器,执行器会有一个AppName,这个要和你在配置文件中写的excutor.appname一样,这样子才能找到

有了执行器之后就是创建任务,创建任务的时候就要指定好对应的执行器以及cron表达式之类的,这里需要注意的就是JobHandler是需要和定时任务上面的注解 @XxlJob() 里面的参数一样的

示例代码:

复制代码
@Component
public class ShopCartJob {

    @Resource
    private IShopCarService shopCarService;

    private final static Logger LOGGER = LoggerFactory.getLogger(ShopCartJob.class);

    /**
     * 删除冷用户购物车缓存数据
     * @return
     */
    @XxlJob("deleteColdUserShopCartCache")
    public void deleteColdUserShopCartCache() {
        try {
            shopCarService.removeColdUserCartInCache();
            XxlJobHelper.log("冷用户购物车缓存清理完成");
        } catch (Exception e) {
            LOGGER.error("请求冷用户购物车缓存数据失败,失败原因", e);
            XxlJobHelper.log("冷用户购物车缓存清理失败:" + e.getMessage());
            throw new RuntimeException(e);
        }
    }

}

个人小总结:

推荐的做法是一个模块一个执行器,一个执行器下面只挂同一个模块的多个任务,因为如果把多个模块的任务都挂到同一个执行器下面,那么xxljob在扫描其中的某一个模块的时候会发现有一些任务并不存在(按照@XxlJob来找),因为这些任务是其他模块的,这个就会出现大量的调用失败,但是实际上最后都是有执行到的,只不过会产生多余的失败日志对信息排查造成影响