分布式任务调度系统
1、contab指令快速实现简单的定时任务
在Linux机器上直接使用crontab -e指令,就可以编辑一个简单的调度任务,适用于请求量不是很大的情况
* * * * * echo "timer">/root/test.out
缺点:只能在本机上执行;只能做到分钟级别的
在配置每一个部分的表达式时,可以指定具体的数值,比如分钟位设置为0,表示每次第0分钟执行一
次。在这些基础数字之外,还允许几个特殊的字符:
- 表示所有可能的值。
- 表示指定范围。例如在day位配置 1-10,表示每个月的第1天到第10天
- ,表示枚举。例如minute位配置 3,5 表示第3分钟和第5分钟执行。
- / 表示指定增量。例如在minute位配置0/15表示从0分钟开始,每15分钟执行一次。3/20表示第三分钟开始,每20分钟执行一次。
另外还有几个用得比较少的特殊符号。例如对于day和week位置。为了避免可能的冲突,就可以将其中一个值设为?
xxljob快速实现轻量级分布式任务调度
实现定时任务的框架有很多,目前在国内用得比较多的是elasticjob和xxljob。这两个项目都有非常多的运用场景。其中elasticjob依赖zookeeper进行分布式任务协调,而xxljob则只需要通过数据库进行协调即可。
案例
package com.xxl.job.service.handler;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.IJobHandler;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpJobHandler extends IJobHandler {
@Override
public void execute() throws Exception {
String target = XxlJobHelper.getJobParam();
XxlJobHelper.log("received job target:"+target);
boolean isPostMethod= false;
// request
HttpURLConnection connection = null;
BufferedReader bufferedReader = null;
try {
// connection
URL realUrl = new URL(target);
connection = (HttpURLConnection) realUrl.openConnection();
connection.setDoOutput(isPostMethod);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setReadTimeout(5 * 1000);
connection.setConnectTimeout(3 * 1000);
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("Content-Type","application/json;charset=UTF-8");
connection.setRequestProperty("Accept-Charset","application/json;charset=UTF-8");
// do connection
connection.connect();
// valid StatusCode
int statusCode = connection.getResponseCode();
if (statusCode != 200) {
throw new RuntimeException("Http Request StatusCode(" +
statusCode + ") Invalid.");
}
// result
bufferedReader = new BufferedReader(new
InputStreamReader(connection.getInputStream(), "UTF-8"));
StringBuilder result = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
result.append(line);
}
String responseMsg = result.toString();
XxlJobHelper.log(responseMsg);
return;
} catch (Exception e) {
XxlJobHelper.log(e);
XxlJobHelper.handleFail();
return;
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if (connection != null) {
connection.disconnect();
}
} catch (Exception e2) {
XxlJobHelper.log(e2);
}
}
}
}
在这个实现中,访问的目标地址target,是作为任务参数传入的。因此,接下来只要在xxljob的任务配置界面将URL以参数形式配置即可。
定时任务的核心-时间轮算法
时间轮算法可以简单的看成由一个循环数组+双向链表
的数据结构
实现。循环数组构成一个环形结构,指针每隔tickDuration时间就走一步。而数组中每个节点上挂载了一个双向链表结构的定时任务列表。
双向链表上的任务有个属性为remainingRounds,表示当前任务剩下的轮次是多少。每当指针走到该任务对应的数据节点上时,remainingRounds就减少1,直到remainingRounds为0时,就触发当前定时任务。从这个原理中可以看到,tickDuration越小,定时任务就越精确,但是响应的,系统的计算负担就越重