项目是spring cloud框架,eureka注册中心,使用许雪里的xxl-job,打算后续将其改造为spring boot工程,接入服务中心。以下是我对其源码接入spring cloud/boot的使用方式,可改良之处尚多,仅给大家提供参考,方便使用。
xxl-job项目地址:
github https://github.com/xuxueli/xxl-job
document http://www.xuxueli.com/xxl-job/
对原项目的介绍此处不再细说,可点开上面的参考文档熟悉
改动后的admin地址:https://github.com/lich1n/my-xxl-job
改动内容:
- 添加了通过业务单号查询job的方法
- 将jobinfo操作的几个接口直接暴露出来了,接我们系统权限验证的我移除了。
- jobinfo表增加业务类型及单号字段
使用方式
将xxl-job-admin打包部署好,自己的业务系统接入job中心启动即可。上面改动过的admin修改mysql连接地址后即可运行,sql脚本为resources下的tables_xxl_job.sql
以下是业务模块的接入过程
spring boot 项目接入步骤
1. 引入依赖
2. yml配置
3. 装配配置类
4. 创建jobhandler执行器
5. 进入xxl-job控制台新增执行器
6. 使用新增的执行器开始任务
7. 在业务模块中操作job的【增、删、改、查、暂停】
1 . 引入依赖
<dependency>
<groupId>com.xuxueligroupId>
<artifactId>xxl-job-coreartifactId>
<version>1.9.1version>
dependency>
2. yml配置
xxl:
job:
admin:
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
addresses: http://localhost:8080
### xxl-job executor address
executor:
appname: mktcenter
ip:
port: 9888
### xxl-job log path
logpath: /data/applogs/xxl-job/jobhandler
### xxl-job log retention days
logretentiondays: -1
### xxl-job, access token
accessToken:
3. 装配配置类
package com.bizvane.mktcenterserviceimpl.common.job;
import com.xxl.job.core.executor.XxlJobExecutor;
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.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.bizvane.mktcenterserviceimpl.service.jobhandler")
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 XxlJobExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobExecutor xxlJobExecutor = new XxlJobExecutor();
xxlJobExecutor.setAdminAddresses(adminAddresses);
xxlJobExecutor.setAppName(appName);
xxlJobExecutor.setIp(ip);
xxlJobExecutor.setPort(port);
xxlJobExecutor.setAccessToken(accessToken);
xxlJobExecutor.setLogPath(logPath);
xxlJobExecutor.setLogRetentionDays(-1);
return xxlJobExecutor;
}
}
4. 创建jobhandler执行器
package com.bizvane.mktcenterserviceimpl.service.jobhandler;
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;
@JobHandler(value="activity")
@Component
public class ActivityJobHandler extends IJobHandler {
@Override
public ReturnT execute(String param) throws Exception {
System.out.println("开始执行生日活动");
return null;
}
}
5. 进入xxl-job控制台新增执行器
6. 使用新增的执行器开始任务
JobHandler名字为步骤4中@JobHandler注解value值
保存即可。
7. 在业务模块中操作job的【增、删、改、查、暂停】等
jobclient工具类,使用restTemplate进行远程调用
package com.bizvane.utils.jobutils;
import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
@Component
public class JobClient {
@Autowired
private RestTemplate restTemplate;
@Value("${xxl.job.admin.addresses}")
private String[] jobAdminUrl;
private static String add = "/jobinfo/add";
private static String update = "/jobinfo/update";
private static String remove = "/jobinfo/remove";
private static String pause = "/jobinfo/pause";
private static String resume = "/jobinfo/resume";
private static String getJobInfoByBiz = "/jobinfo/getJobInfoByBiz";
public JobClient() {
}
public ResponseEntity addJob(XxlJobInfo xxlJobInfo) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap xxlJobInfoMap = MapUtil.obj2Map(xxlJobInfo);
HttpEntity> request = new HttpEntity(xxlJobInfoMap, headers);
ResponseEntity response = this.restTemplate.postForEntity(this.getLoadUrl(add), request, String.class, new Object[0]);
return response;
}
public ResponseEntity updateJob(XxlJobInfo xxlJobInfo) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap xxlJobInfoMap = MapUtil.obj2Map(xxlJobInfo);
HttpEntity> request = new HttpEntity(xxlJobInfoMap, headers);
ResponseEntity response = this.restTemplate.postForEntity(this.getLoadUrl(update), request, String.class, new Object[0]);
return response;
}
public ResponseEntity removeJob(Integer xxlJobInfoId) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity request = new HttpEntity(xxlJobInfoId, headers);
ResponseEntity response = this.restTemplate.postForEntity(this.getLoadUrl(remove), request, String.class, new Object[0]);
return response;
}
public ResponseEntity pauseJob(int xxlJobInfoId) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity request = new HttpEntity(xxlJobInfoId, headers);
ResponseEntity response = this.restTemplate.postForEntity(this.getLoadUrl(pause), request, String.class, new Object[0]);
return response;
}
public ResponseEntity resumeJob(int xxlJobInfoId) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity request = new HttpEntity(xxlJobInfoId, headers);
ResponseEntity response = this.restTemplate.postForEntity(this.getLoadUrl(resume), request, String.class, new Object[0]);
return response;
}
public ResponseEntity getJobInfoByBizJob(XxlJobInfo xxlJobInfo) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap xxlJobInfoMap = MapUtil.obj2Map(xxlJobInfo);
HttpEntity> request = new HttpEntity(xxlJobInfoMap, headers);
ResponseEntity response = this.restTemplate.postForEntity(this.getLoadUrl(getJobInfoByBiz), request, String.class, new Object[0]);
return response;
}
public String getLoadUrl(String method) {
int length = this.jobAdminUrl.length;
Random random = new Random();
int i = random.nextInt(length);
String url = this.jobAdminUrl[i] + method;
return url;
}
}
map工具类
package com.bizvane.utils.jobutils;
import java.lang.reflect.Field;
import java.util.Collections;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
public class MapUtil {
public MapUtil() {
}
public static MultiValueMap obj2Map(Object obj) {
MultiValueMap map = new LinkedMultiValueMap();
Field[] fields = obj.getClass().getDeclaredFields();
int i = 0;
for(int len = fields.length; i < len; ++i) {
String varName = fields[i].getName();
try {
boolean accessFlag = fields[i].isAccessible();
fields[i].setAccessible(true);
Object o = fields[i].get(obj);
if (o != null) {
map.put(varName, Collections.singletonList(o.toString()));
}
fields[i].setAccessible(accessFlag);
} catch (IllegalArgumentException var8) {
var8.printStackTrace();
} catch (IllegalAccessException var9) {
var9.printStackTrace();
}
}
return map;
}
public static MultiValueMap obj2MapWithNull(Object obj) {
MultiValueMap map = new LinkedMultiValueMap();
Field[] fields = obj.getClass().getDeclaredFields();
int i = 0;
for(int len = fields.length; i < len; ++i) {
String varName = fields[i].getName();
try {
boolean accessFlag = fields[i].isAccessible();
fields[i].setAccessible(true);
Object o = fields[i].get(obj);
if (o != null) {
map.put(varName, Collections.singletonList(o.toString()));
} else {
map.put(varName, (Object)null);
}
fields[i].setAccessible(accessFlag);
} catch (IllegalArgumentException var8) {
var8.printStackTrace();
} catch (IllegalAccessException var9) {
var9.printStackTrace();
}
}
return map;
}
public static MultiValueMap obj2MapWithString(Object obj) {
MultiValueMap map = new LinkedMultiValueMap();
Field[] fields = obj.getClass().getDeclaredFields();
int i = 0;
for(int len = fields.length; i < len; ++i) {
String varName = fields[i].getName();
try {
boolean accessFlag = fields[i].isAccessible();
fields[i].setAccessible(true);
Object o = fields[i].get(obj);
if (o != null) {
map.put(varName, Collections.singletonList(o.toString()));
} else {
map.put(varName, Collections.singletonList(""));
}
fields[i].setAccessible(accessFlag);
} catch (IllegalArgumentException var8) {
var8.printStackTrace();
} catch (IllegalAccessException var9) {
var9.printStackTrace();
}
}
return map;
}
}
添加了业务类型及code的实体类
package com.bizvane.utils.jobutils;
import java.util.Date;
public class XxlJobInfo {
private int id;
private int jobGroup;
private String jobCron;
private String jobDesc;
private Date addTime;
private Date updateTime;
private String author;
private String alarmEmail;
private String executorRouteStrategy;
private String executorHandler;
private String executorParam;
private String executorBlockStrategy;
private String executorFailStrategy;
private int executorTimeout;
private String glueType;
private String glueSource;
private String glueRemark;
private Date glueUpdatetime;
private String childJobId;
private String jobStatus;
private String appName;
private Integer bizType;
private String bizCode;
public XxlJobInfo() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public int getJobGroup() {
return this.jobGroup;
}
public void setJobGroup(int jobGroup) {
this.jobGroup = jobGroup;
}
public String getJobCron() {
return this.jobCron;
}
public void setJobCron(String jobCron) {
this.jobCron = jobCron;
}
public String getJobDesc() {
return this.jobDesc;
}
public void setJobDesc(String jobDesc) {
this.jobDesc = jobDesc;
}
public Date getAddTime() {
return this.addTime;
}
public void setAddTime(Date addTime) {
this.addTime = addTime;
}
public Date getUpdateTime() {
return this.updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getAuthor() {
return this.author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getAlarmEmail() {
return this.alarmEmail;
}
public void setAlarmEmail(String alarmEmail) {
this.alarmEmail = alarmEmail;
}
public String getExecutorRouteStrategy() {
return this.executorRouteStrategy;
}
public void setExecutorRouteStrategy(String executorRouteStrategy) {
this.executorRouteStrategy = executorRouteStrategy;
}
public String getExecutorHandler() {
return this.executorHandler;
}
public void setExecutorHandler(String executorHandler) {
this.executorHandler = executorHandler;
}
public String getExecutorParam() {
return this.executorParam;
}
public void setExecutorParam(String executorParam) {
this.executorParam = executorParam;
}
public String getExecutorBlockStrategy() {
return this.executorBlockStrategy;
}
public void setExecutorBlockStrategy(String executorBlockStrategy) {
this.executorBlockStrategy = executorBlockStrategy;
}
public String getExecutorFailStrategy() {
return this.executorFailStrategy;
}
public void setExecutorFailStrategy(String executorFailStrategy) {
this.executorFailStrategy = executorFailStrategy;
}
public int getExecutorTimeout() {
return this.executorTimeout;
}
public void setExecutorTimeout(int executorTimeout) {
this.executorTimeout = executorTimeout;
}
public String getGlueType() {
return this.glueType;
}
public void setGlueType(String glueType) {
this.glueType = glueType;
}
public String getGlueSource() {
return this.glueSource;
}
public void setGlueSource(String glueSource) {
this.glueSource = glueSource;
}
public String getGlueRemark() {
return this.glueRemark;
}
public void setGlueRemark(String glueRemark) {
this.glueRemark = glueRemark;
}
public Date getGlueUpdatetime() {
return this.glueUpdatetime;
}
public void setGlueUpdatetime(Date glueUpdatetime) {
this.glueUpdatetime = glueUpdatetime;
}
public String getChildJobId() {
return this.childJobId;
}
public void setChildJobId(String childJobId) {
this.childJobId = childJobId;
}
public String getJobStatus() {
return this.jobStatus;
}
public void setJobStatus(String jobStatus) {
this.jobStatus = jobStatus;
}
public String getAppName() {
return this.appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public Integer getBizType() {
return this.bizType;
}
public void setBizType(Integer bizType) {
this.bizType = bizType;
}
public String getBizCode() {
return this.bizCode;
}
public void setBizCode(String bizCode) {
this.bizCode = bizCode;
}
}
再封装一个工具类使用jobclient,下面给出了一个添加job的操作
package com.bizvane.mktcenterserviceimpl.common.utils;
import com.bizvane.mktcenterservice.models.po.MktTaskPOWithBLOBs;
import com.bizvane.mktcenterservice.models.vo.ActivitySmartVO;
import com.bizvane.mktcenterservice.models.vo.ActivityVO;
import com.bizvane.mktcenterserviceimpl.common.constants.JobHandlerConstants;
import com.bizvane.mktcenterserviceimpl.common.enums.BusinessTypeEnum;
import com.bizvane.mktcenterserviceimpl.common.job.XxlJobConfig;
import com.bizvane.utils.enumutils.JobEnum;
import com.bizvane.utils.jobutils.JobClient;
import com.bizvane.utils.jobutils.XxlJobInfo;
import com.bizvane.utils.tokens.SysAccountPO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @author chen.li
* @date on 2018/7/4 9:50
* @description
*/
@Component
public class JobUtil {
@Autowired
private XxlJobConfig xxlJobConfig;
@Autowired
private JobClient jobClient;
/**
* 通用job添加方法
* @param execuDate
* @param desc
* @param param
* @param author
* @param jobHandler
* @param businessType
*/
public void addJob(Date execuDate,String desc,String param,String author,String jobHandler,int businessType){
//构建job对象
XxlJobInfo xxlJobInfo = new XxlJobInfo();
//设置appName
xxlJobInfo.setAppName(xxlJobConfig.getAppName());
//设置路由策略
xxlJobInfo.setExecutorRouteStrategy(JobEnum.EXECUTOR_ROUTE_STRATEGY_FIRST.getValue());
//设置job定时器
xxlJobInfo.setJobCron(DateUtil.getCronExpression(execuDate));
//设置运行模式
xxlJobInfo.setGlueType(JobEnum.GLUE_TYPE_BEAN.getValue());
//设置job处理器
xxlJobInfo.setExecutorHandler(jobHandler);
//设置job描述
xxlJobInfo.setJobDesc(desc);
//设置执行参数
xxlJobInfo.setExecutorParam(param);
//设置阻塞处理策略
xxlJobInfo.setExecutorBlockStrategy(JobEnum.EXECUTOR_BLOCK_SERIAL_EXECUTION.getValue());
//设置失败处理策略
xxlJobInfo.setExecutorFailStrategy(JobEnum.EXECUTOR_FAIL_STRATEGY_NULL.getValue());
//设置负责人
xxlJobInfo.setAuthor(author);
//设置业务类型
xxlJobInfo.setBizType(businessType);
//添加job
jobClient.addJob(xxlJobInfo);
}
}
至此将job-admin启动起来,自己的业务模块启动起来,就可通过业务操作对job中心的job进行更改,也可以直接在job平台直观操作job控制业务模块的jobhandler的执行。由于restTemplate的loadBalance注解会产生问题,具体问题我没有细查,因此在启动类上需要注释掉此注解,于是我手动在上面jobClient最下方中做了简单的请求负载。以下是启动类代码
package com.bizvane.mktcenterserviceimpl;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication(scanBasePackages = "com.bizvane")
@EnableDiscoveryClient
@EnableSwagger2
@EnableFeignClients(basePackages={"com.bizvane.centerstageservice.rpc","com.bizvane.members.facade.service.api","com.bizvane.couponfacade.interfaces"})
@MapperScan("com.bizvane.mktcenterserviceimpl.mappers")
public class MktcenterApplication {
// @Value("${swagger.show}")
// private boolean swaggerShow;
// @LoadBalanced
@Bean
RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
public static void main(String[] args) {
SpringApplication.run(MktcenterApplication.class, args);
}
}