很多业务场景需要我们某一特定的时刻去做某件任务,定时任务解决的就是这种业务场景。一般来说,系统可以使用消息传递代替部分定时任务,两者有很多相似之处,可以相互替换场景。如,上面发货成功发短信通知客户的业务场景,我们可以在发货成功后发送MQ消息到队列,然后去消费mq消息,发送短信。
但在某些场景下不能互换:
a)时间驱动/事件驱动:内部系统一般可以通过时间来驱动,但涉及到外部系统,则只能使用时间驱动。如怕取外部网站价格,每小时爬一次
b)批量处理/逐条处理:批量处理堆积的数据更加高效,在不需要实时性的情况下比消息中间件更有优势。而且有的业务逻辑只能批量处理。如移动每个月结算我们的话费
c)实时性/非实时性:消息中间件能够做到实时处理数据,但是有些情况下并不需要实时,比如:vip升级
d)系统内部/系统解耦:定时任务调度一般是在系统内部,而消息中间件可用于两个系统间
如果你的项目后期部署到集群环境下,如果不做处理,就会出现意想不到的问题,原因:由于我们项目同时部署在多台集群机器上,因此到达指定的定时时间时,多台机器上的定时器可能会同时启动,造成重复数据或者程序异常等问题
elastic-job的上次更新时间是两年前, 所以我们果断使用xxl-job了.
这次下载的是2.1.0的版本
com.xuxueli
xxl-job-core
2.1.0
# web port
server.port=8081
# log config
logging.config=classpath:logback.xml
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### xxl-job executor address
xxl.job.executor.appname=xxl-job-executor-sample
xxl.job.executor.ip=
xxl.job.executor.port=9999
### xxl-job, access token
xxl.job.accessToken=
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job log retention days
xxl.job.executor.logretentiondays=-1
logback
%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
${log.path}
${log.path}.%d{yyyy-MM-dd}.zip
%date %level [%thread] %logger{36} [%file : %line] %msg%n
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.executor.appname}")
private String appName;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppName(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
/**
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
*
* 1、引入依赖:
*
* org.springframework.cloud
* spring-cloud-commons
* ${version}
*
*
* 2、配置文件,或者容器启动变量
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、获取IP
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import org.springframework.stereotype.Component;
/**
* @author zk
* @Description:
* @date 2019-10-09 9:47
*/
@Component
@JobHandler("myJobhandler2")
public class MyJobhandler extends IJobHandler {
@Override
public ReturnT execute(String param) throws Exception {
System.out.println("这是自定义的第二个任务" + param);
return ReturnT.SUCCESS;
}
}
分别是cron 表达式, jobhandker, 就是自定义的一个jobhandler, 名字对应就可以了.
执行就是把这个任务执行一次, 启动就是直接启动这个定时任务了, 根据cron表达式去执行.
调度中心集群
调度中心支持集群部署,提升调度系统容灾和可用性。
调度中心集群部署时,几点要求和建议:
在原来服务的基础上新建一个启动文件, 注释掉原项目中的端口信息,才用配置文件的形式.增加token,其他不改动.
就可以启动两个调度中心了.在任意一个调度中心上新建一个任务, 另一个都会有的.
执行器集群
执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。
执行器集群部署时,几点要求和建议:
Web 的端口号和executor的端口号都不能重复.
修改配置文件,添加多个调度中心的地址, 以及token,修改端口号,注释掉端口号
新建一个springboot 启动类,然后启动两个服务,开始执行定时任务,执行了3次任务, 都发到同一个客户端执行了, 没有问题,在关闭了一个客户端之后,再次执行,任务失败. 过了一会,再执行,任务成功, 已经自动切换到另外一台应用上了,关闭一个调度中心也是没有问题的.