公司项目是分布式的,所以定时任务用lts框架,简单的看看代码,分析了一下,找到调用流程。
注意:不了解lts的先看下这个文档,我也是看完才看懂代码
文档地址:https://www.cnblogs.com/dion-90/articles/8674591.html
lts的工作流程
- JobClient 提交一个 任务 给 JobTracker, 这里我提供了两种客户端API, 一种是如果JobTracker 不存在或者提交失败,直接返回提交失败。另一种客户端是重试客户端, 如果提交失败,先存储到本地leveldb(可以使用NFS来达到同个节点组共享leveldb文件的目的,多线程访问,做了文件锁处理),返回给客户端提交成功的信息,待JobTracker可用的时候,再将任务提交。
- JobTracker 收到JobClient提交来的任务,先生成一个唯一的JobID。然后将任务储存在Mongo集群中。JobTracker 发现有(任务执行的)可用的TaskTracker节点(组) 之后,将优先级最大,最先提交的任务分发给TaskTracker。这里JobTracker会优先分配给比较空闲的TaskTracker节点,达到负载均衡。
- TaskTracker 收到JobTracker分发来的任务之后,执行。执行完毕之后,再反馈任务执行结果给JobTracker(成功or 失败[失败有失败错误信息]),如果发现JobTacker不可用,那么存储本地leveldb,等待TaskTracker可用的时候再反馈。反馈结果的同时,询问JobTacker有没有新的任务要执行。
- JobTacker收到TaskTracker节点的任务结果信息,生成并插入(mongo)任务执行日志。根据任务信息决定要不要反馈给客户端。不需要反馈的直接删除, 需要反馈的(同样JobClient不可用存储文件,等待可用重发)。
- JobClient 收到任务执行结果,进行自己想要的逻辑处理。
代码分析
以我公司为例, spring cloud里有两个服务分别是JobClient
和JobTracker
:
其中task相当于JobClient, job相当于JobTracker
1.task中的代码, 项目启动运行这个方法, 这相当是定制任务的基本信息:
// task中的代码, 项目启动运行这个方法
/**
* 定时生成 ...数据
*/
public void jobMethod()
{
// 定义个bean, 里面可以定义属性
SynMainTainMsgCommand command = new SynMainTainMsgCommand();
// 定义任务对象
Job job = new Job();
// taskId自定义
job.setTaskId("doMainTainProdcut");
// 自定义参数,实际就是HashMap
job.setParam("description", "定时生成 车辆 保养数据");
// 注意这里传的commond对象的类全名
job.setParam("command", command.getClass().getName());
job.setTaskTrackerNodeGroup("组名");
job.setNeedFeedback(true);
job.setReplaceOnExist(true); // 当任务队列中存在这个任务的时候,是否替换更新
job.setCronExpression("cron表达式");
// 提交任务
Response response = jobClient.submitJob(job);
}
2.先把job任务写了,一会再说怎么调用的
在job服务里实现相应的任务:
@Component
public class SynMainTainMsgHandler extends BaseCommandHandler<SynMainTainMsgCommand, HttpCommandResultWithData> {
// ...略
public HttpCommandResultWithData handler(SynMainTainMsgCommand command) {
// 具体的任务实现...
}
}
3.关键是下一步,job怎么收到并且执行的
// 1.实现JobRunner的run方法
@JobRunner4TaskTracker
public class JobRunnerImpl implements JobRunner {
@Override
public Result run(JobContext jobContext) throws Throwable {
String successjson;
try {
// 接到参数
String command = jobContext.getJob().getParam("command");
String commandParam = jobContext.getJob().getParam("commandParam");
// 反射找到上面定义的bean
Class clazz = Class.forName(command);
// ...略部分代码
Object o = clazz.newInstance();
// 这里想通过command找handler,并执行
Command.Result handlerresult = dispatch((AbstractCommand)o);
// 这里只要找到 BaseCommandHandler的handler方法执行就行了,因为上面的SynMainTainMsgHandler继承了BaseCommandHandler
successjson = JsonUtil.toJson(handlerresult);
return new Result(Action.EXECUTE_SUCCESS, successjson);
}
}
- 这里只要找到 BaseCommandHandler的handler方法执行就行了,因为上面的SynMainTainMsgHandler继承了BaseCommandHandler,
BaseCommandHandler继承CommandHandler。
private <C extends Command<?>, CR extends Command.Result> CR dispatch(C command) {
CommandHandler<C, CR> commandHandler = (CommandHandler)this.handlerByType.get(command.getClass());
return commandHandler.handle(command);
}
省略部分代码,实现思路就是通过传进来的command.className获取到具体的handler。