背景介绍
公司业务中需要使用到定时任务,前期我们使用的是Quartz,可以满足前期的业务需求(定时任务执行不稳定,能力有限,目前还未查到具体原因,经过测试发现和线程有部分关系),后期随着业务量骤增,服务端需要做集群,这时发现Quartz对集群的支持并不友好,因此我们又找到了Xxl-Job这款定时器以满足搭建集群的目的,Xxl-Job有自己的管理界面,但是我们自己的业务定时任务时由用户自定义搭建的,那么怎样将用户编辑的定时任务同步给Xxl-Job服务呢?这时就需要通过API接口或者其他方式来打通自己的服务和Xxl-Job服务,然而Xxl-Job的文档中并没有提供API,因此就需要我们自己想办法,废话到此,上代码!!!
第一步:登录获取Cookie,因为安全问题,Xxl-Job要求所有业务接口的请求都需要携带Cookie,否则直接访问会跳转回登录界面
@Slf4j
@Component
@RequiredArgsConstructor
public class XxJobLoginInit implements PipelineHandler {
private final XxlJobServiceApi xxlJobServiceApi;
@Override
public void receive(HandlerContext ctx, Fluid<?> t) {
xxlJobServiceApi.loginXxlJob();
PipelineHandler.super.receive(ctx, t);
}
}
第二步:列举我们要用到的API接口
public class XxlJobServiceUrl {
public static final String loginXxlJob = "/login";
public static final String addXxlJob = "/jobinfo/add";
public static final String updateXxlJob = "/jobinfo/update";
public static final String startXxlJob = "/jobinfo/start";
public static final String stopXxlJob = "/jobinfo/stop";
public static final String removeXxlJob = "/jobinfo/remove";
public static final String findXxlJobPage = "/jobgroup/pageList";
}
第三步:进行业务接口封装,例如增加、删除、查询、启用、禁用定时任务
@Slf4j
@Component
@RequiredArgsConstructor
public class XxlJobServiceApi {
@Value("${xxl.job.userName}")
private String userName;
@Value("${xxl.job.password}")
private String password;
@Value("${xxl.job.admin.addresses}")
private String basicUrl;
@Value("${xxl.job.executor.appname}")
private String appname;
private final Map<String, String> loginCookie = new HashMap<>();
/**
* 登录接口
*/
public String loginXxlJob() {
HttpResponse response = HttpRequest.post(basicUrl + XxlJobServiceUrl.loginXxlJob).form("userName", userName).form("password", password).execute();
List<HttpCookie> cookies = response.getCookies();
Optional<HttpCookie> cookieOpt = cookies.stream().filter(cookie -> cookie.getName().equals("XXL_JOB_LOGIN_IDENTITY")).findFirst();
if (!cookieOpt.isPresent()) {
throw new RuntimeException("xxlJob登录失败!");
}
String xxlJobCookie = cookieOpt.get().getValue();
loginCookie.put("XXL_JOB_LOGIN_IDENTITY", xxlJobCookie);
return xxlJobCookie;
}
/**
* 查询定时任务执行器
*/
public String findXxlJobGroup() {
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("appname", appname);
paramMap.add("start", "0");
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
headers.add("Cookie", !ObjectUtil.isEmpty(cookieStr) ? cookieStr : loginXxlJob());
Map map = JSON.parseObject(HttpUtil.post(basicUrl + XxlJobServiceUrl.findJobGroup, paramMap, headers), HashMap.class);
if (!ObjectUtil.isEmpty(map) && map.containsKey("data") && !ObjectUtil.isEmpty(map.get("data"))) {
List<Map<String, Object>> mapList = (List<Map<String, Object>>) map.get("data");
return mapList.get(0).get("id").toString();
}
throw new RuntimeException("xxlJob执行器获取失败!");
}
/**
* 新增定时任务
* 返回值的content代表主键ID
*/
public Map addXxlJob(String jobDesc, String cron, String jobName, String param) {
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("jobDesc", jobDesc);
paramMap.add("scheduleConf", cron);
paramMap.add("cronGen_display", cron);
paramMap.add("schedule_conf_CRON", cron);
paramMap.add("executorHandler", jobName);
paramMap.add("executorParam", param);
paramMap.add("jobGroup", "2");
paramMap.add("author", "admin");
paramMap.add("scheduleType", "CRON");
paramMap.add("glueType", "BEAN");
paramMap.add("executorRouteStrategy", "FIRST");
paramMap.add("misfireStrategy", "DO_NOTHING");
paramMap.add("executorBlockStrategy", "SERIAL_EXECUTION");
paramMap.add("executorTimeout", "0");
paramMap.add("executorFailRetryCount", "0");
paramMap.add("glueRemark", "GLUE代码初始化");
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
headers.add("Cookie", !ObjectUtil.isEmpty(cookieStr) ? "XXL_JOB_LOGIN_IDENTITY=" + cookieStr : "XXL_JOB_LOGIN_IDENTITY=" + loginXxlJob());
return JSON.parseObject(HttpUtil.post(basicUrl + XxlJobServiceUrl.addXxlJob, paramMap, headers), HashMap.class);
}
/**
* 编辑定时任务
*/
public Map updateXxlJob(String jobId, String jobDesc, String cron, String jobName, String param) {
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("id", jobId);
paramMap.add("jobDesc", jobDesc);
paramMap.add("scheduleConf", cron);
paramMap.add("cronGen_display", cron);
paramMap.add("schedule_conf_CRON", cron);
paramMap.add("executorHandler", jobName);
paramMap.add("executorParam", param);
paramMap.add("jobGroup", "2");
paramMap.add("author", "admin");
paramMap.add("scheduleType", "CRON");
paramMap.add("glueType", "BEAN");
paramMap.add("executorRouteStrategy", "FIRST");
paramMap.add("misfireStrategy", "DO_NOTHING");
paramMap.add("executorBlockStrategy", "SERIAL_EXECUTION");
paramMap.add("executorTimeout", "0");
paramMap.add("executorFailRetryCount", "0");
paramMap.add("glueRemark", "GLUE代码初始化");
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
headers.add("Cookie", !ObjectUtil.isEmpty(cookieStr) ? "XXL_JOB_LOGIN_IDENTITY=" + cookieStr : "XXL_JOB_LOGIN_IDENTITY=" + loginXxlJob());
return JSON.parseObject(HttpUtil.post(basicUrl + XxlJobServiceUrl.updateXxlJob, paramMap, headers), HashMap.class);
}
/**
* 启动定时任务
*/
public Map startXxlJob(String jobId) {
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("id", jobId);
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
headers.add("Cookie", !ObjectUtil.isEmpty(cookieStr) ? "XXL_JOB_LOGIN_IDENTITY=" + cookieStr : "XXL_JOB_LOGIN_IDENTITY=" + loginXxlJob());
return JSON.parseObject(HttpUtil.post(basicUrl + XxlJobServiceUrl.startXxlJob, paramMap, headers), HashMap.class);
}
/**
* 暂停定时任务
*/
public Map stopXxlJob(String jobId) {
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("id", jobId);
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
headers.add("Cookie", !ObjectUtil.isEmpty(cookieStr) ? "XXL_JOB_LOGIN_IDENTITY=" + cookieStr : "XXL_JOB_LOGIN_IDENTITY=" + loginXxlJob());
return JSON.parseObject(HttpUtil.post(basicUrl + XxlJobServiceUrl.stopXxlJob, paramMap, headers), HashMap.class);
}
/**
* 删除定时任务
*/
public Map removeXxlJob(String jobId) {
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("id", jobId);
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
headers.add("Cookie", !ObjectUtil.isEmpty(cookieStr) ? "XXL_JOB_LOGIN_IDENTITY=" + cookieStr : "XXL_JOB_LOGIN_IDENTITY=" + loginXxlJob());
return JSON.parseObject(HttpUtil.post(basicUrl + XxlJobServiceUrl.removeXxlJob, paramMap, headers), HashMap.class);
}
/**
* 查询任务列表
*/
public Map findXxlJobPage() {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
headers.add("Cookie", !ObjectUtil.isEmpty(cookieStr) ? "XXL_JOB_LOGIN_IDENTITY=" + cookieStr : "XXL_JOB_LOGIN_IDENTITY=" + loginXxlJob());
return JSON.parseObject(HttpUtil.post(basicUrl + XxlJobServiceUrl.removeXxlJob, headers), HashMap.class);
}
第四步:实际业务调用
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(rollbackOn = Exception.class)
public class PushConfigServiceImpl implements PushConfigService {
private final XxlJobServiceApi xxlJobServiceApi;
private final PushConfigRepository pushConfigRepository;
@Override
public void deleteById(Long configId) {
PushConfigDO pushConfigDO= pushConfigRepository.findByConfigId(configId);
if (!ObjectUtil.isEmpty(pushConfigDO)) {
pushConfigDO.setDeleted(true);
pushConfigRepository.save(pushConfigDO);
}
Map resultMap = xxlJobServiceApi.removeXxlJob(pushConfigDO.getXxlJobId());
if (ObjectUtil.isEmpty(resultMap) || !resultMap.containsKey("code") || !resultMap.get("code").equals(200)) {
throw new BizException("删除推送配置失败,请检查!");
}
}
}