如何在项目中集成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);@Beanpublic 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();@Datapublic static class Admin {private String addresses;}@Datapublic 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-adminaccessToken:executor:appname: livingjobHandleraddress:ip:port: 9999logpath: ./logs/xxl-joblogretentiondays: 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 {@Resourceprivate 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来找),因为这些任务是其他模块的,这个就会出现大量的调用失败,但是实际上最后都是有执行到的,只不过会产生多余的失败日志对信息排查造成影响