项目中需要后端以 无入侵
的方式,调用调度中心API服务。然而调度中心设置了登录,调度中心API接口对cookie进行了验证,feign访问调度中心API服务时,需通过其登录验证。
通过FeignClient客户端声明式调用调度中心Api服务与普通FeignClient相比作了一下几点处理:
image.png
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.0</version>
</dependency>
2.HttpResultForXxlJob.java
import com.alibaba.fastjson.JSON;
import lombok.Data;
import java.io.Serializable;
/**
* xxl-job Api接口响应包装类
*
* @author liudong
* @date 2021/4/25 16:37
*/
@Data
public class HttpResultForXxlJob<T> implements Serializable {
private static final long serialVersionUID = 6512789515344894483L;
/**
* 请求状态码
*/
private int code;
/**
* 消息
*/
private String msg;
/**
* 返回数据信息
*/
private T content;
/**
* 序列化为Json
*
* @return json字符串
*/
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
import com.alibaba.fastjson.JSONObject;
import com.gaodun.pms.cdp.common.dto.external.xxljob.HttpResultForXxlJob;
import feign.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* xxl-job客户端
*
* @author liudong
* @date 2021/4/25 16:33
*/
@FeignClient(name = "xxlJobClient", url = "${third-party.config.xxl-job.host:not found xxl-job service url}")
public interface XxlJobClient {
/**
* xxl-job登录接口
*
* @param params 参数
* @return 响应信息
*/
@PostMapping(value = "/xxl-job-admin/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
Response login(@RequestBody Map<String, ?> params);
/**
* 创建定时任务
*
* @param cookie cookie
* @param params 定时任务参数
* @return 定时任务ID
*/
@PostMapping(value = "/xxl-job-admin/jobinfo/add", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
HttpResultForXxlJob<Integer> add(@RequestHeader("Cookie") String cookie, @RequestBody Map<String, ?> params);
/**
* 更新定时任务
*
* @param cookie cookie
* @param params 定时任务更新参数
* @return 执行结果
*/
@PutMapping(value = "/xxl-job-admin/jobinfo/update", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
HttpResultForXxlJob<String> update(@RequestHeader("Cookie") String cookie, @RequestBody Map<String, ?> params);
/**
* 删除定时任务
*
* @param cookie cookie
* @param id 定时任务更新参数
* @return 执行结果
*/
@DeleteMapping(value = "/xxl-job-admin/jobinfo/remove")
HttpResultForXxlJob<String> remove(@RequestHeader("Cookie") String cookie, @RequestParam("id") int id);
/**
* 开启任务
*
* @param cookie cookie
* @param id 定时任务ID
* @return 执行结果
*/
@PutMapping(value = "/xxl-job-admin/jobinfo/start")
HttpResultForXxlJob<String> start(@RequestHeader("Cookie") String cookie, @RequestParam("id") int id);
/**
* 结束任务
*
* @param cookie cookie
* @param id 定时任务ID
* @return 执行结果
*/
@PutMapping(value = "/xxl-job-admin/jobinfo/stop")
HttpResultForXxlJob<String> stop(@RequestHeader("Cookie") String cookie, @RequestParam("id") int id);
/**
* 结束任务
*
* @param cookie cookie
* @param params 查询参数
* @return 执行结果
*/
@GetMapping(value = "/xxl-job-admin/joblog/pageList")
JSONObject log(@RequestHeader("Cookie") String cookie, @RequestParam("params") Map<String, Object> params);
}
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gaodun.pms.cdp.common.constant.CacheConstant;
import com.gaodun.pms.cdp.common.constant.XxlJobConstant;
import com.gaodun.pms.cdp.common.dto.external.xxljob.HttpResultForXxlJob;
import com.gaodun.pms.cdp.common.dto.xxljob.XxlJobLogDTO;
import com.gaodun.pms.cdp.common.exception.ErrorCode;
import com.gaodun.pms.cdp.common.request.xxljob.AddOrUpdateXxlJobInfoRequest;
import com.gaodun.pms.cdp.service.acm.ApplicationConfig;
import com.gaodun.pms.cdp.service.acm.JobConfig;
import com.gaodun.pms.cdp.service.external.feign.XxlJobClient;
import com.gaodunwangxiao.pms.exception.AbstractAssert;
import com.gaodunwangxiao.pms.exception.ExceptionFactory;
import com.gaodunwangxiao.pms.exception.SystemException;
import feign.FeignException;
import feign.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 任务管理处理器
*
* @author liudong
* @date 2021/4/25 10:56
*/
@Slf4j
@Component
public class XxlJobComponent {
/**
* xxl job 账号
*/
@Value("${xxl.job.user-name}")
private String userName;
/**
* xxl job 密码
*/
@Value("${xxl.job.password}")
private String password;
/**
* xxl-job客户端
*/
@Resource
private XxlJobClient xxlJobClient;
/**
* 应用全局配置
*/
@Resource
private ApplicationConfig applicationConfig;
/**
* redis操作类
*/
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 登录xxl-job
*/
public void login() {
Map<String, Object> userInfo = new HashMap<>(MapUtil.DEFAULT_INITIAL_CAPACITY);
userInfo.put("userName", userName);
userInfo.put("password", password);
// 设置cookie永久有效,对应xxl-job记住密码
userInfo.put("ifRemember", "on");
Response response = xxlJobClient.login(userInfo);
if (HttpStatus.HTTP_OK == response.status()) {
response.headers().get(XxlJobConstant.COOKIE_KEY).forEach(e -> {
if (e.contains(XxlJobConstant.XXL_JOB_LOGIN_IDENTITY)) {
redisTemplate.opsForValue().set(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY, e, CacheConstant.XXL_JOB_COOKIE_REDIS_TIMEOUT, TimeUnit.HOURS);
}
});
} else {
throw ExceptionFactory.systemException(ErrorCode.LOGIN_XXL_JOB_FAILURE_EXCEPTION);
}
}
/**
* 创建任务
*
* @param addOrUpdateXxlJobInfoRequest 任务参数
* @return 任务ID
*/
public Integer add(AddOrUpdateXxlJobInfoRequest addOrUpdateXxlJobInfoRequest) {
if (Boolean.FALSE.equals(redisTemplate.hasKey(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY))) {
login();
}
if (ObjectUtils.isNotEmpty(addOrUpdateXxlJobInfoRequest)) {
HttpResultForXxlJob result = xxlJobClient.add(String.valueOf(redisTemplate.opsForValue().get(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY)), BeanUtil.beanToMap(addOrUpdateXxlJobInfoRequest));
if (HttpStatus.HTTP_OK == result.getCode()) {
log.info(result.getMsg());
return (int) result.getContent();
}
}
return null;
}
/**
* 更新任务
*
* @param addOrUpdateXxlJobInfoRequest 任务参数
*/
public void update(AddOrUpdateXxlJobInfoRequest addOrUpdateXxlJobInfoRequest) {
if (Boolean.FALSE.equals(redisTemplate.hasKey(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY))) {
login();
}
if (ObjectUtils.isNotEmpty(addOrUpdateXxlJobInfoRequest)) {
HttpResultForXxlJob result = xxlJobClient.update(String.valueOf(redisTemplate.opsForValue().get(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY)), BeanUtil.beanToMap(addOrUpdateXxlJobInfoRequest));
log.info(result.getMsg());
AbstractAssert.isTrue(HttpStatus.HTTP_OK == result.getCode(), ErrorCode.UPDATE_JOB_FAILURE_EXCEPTION);
}
}
/**
* 删除任务
*
* @param id 任务ID
*/
public void remove(Integer id) {
if (Boolean.FALSE.equals(redisTemplate.hasKey(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY))) {
login();
}
if (ObjectUtils.isNotEmpty(id)) {
HttpResultForXxlJob result = xxlJobClient.remove(String.valueOf(redisTemplate.opsForValue().get(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY)), id);
log.info(result.getMsg());
AbstractAssert.isTrue(HttpStatus.HTTP_OK == result.getCode(), ErrorCode.REMOVE_JOB_FAILURE_EXCEPTION);
}
}
/**
* 启动任务
*
* @param id 任务ID
*/
public void start(Integer id) {
if (Boolean.FALSE.equals(redisTemplate.hasKey(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY))) {
login();
}
if (ObjectUtils.isNotEmpty(id)) {
HttpResultForXxlJob result = xxlJobClient.start(String.valueOf(redisTemplate.opsForValue().get(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY)), id);
log.info(result.getMsg());
AbstractAssert.isTrue(HttpStatus.HTTP_OK == result.getCode(), ErrorCode.START_JOB_FAILURE_EXCEPTION);
}
}
/**
* 停止任务
*
* @param id 任务ID
*/
public void stop(Integer id) {
if (Boolean.FALSE.equals(redisTemplate.hasKey(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY))) {
login();
}
if (ObjectUtils.isNotEmpty(id)) {
HttpResultForXxlJob result = xxlJobClient.stop(String.valueOf(redisTemplate.opsForValue().get(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY)), id);
log.info(result.getMsg());
AbstractAssert.isTrue(HttpStatus.HTTP_OK == result.getCode(), ErrorCode.STOP_JOB_FAILURE_EXCEPTION);
}
}
/**
* 查询日志
*
* @param params 查询参数
* @return 结果集
*/
@Retryable(value = SystemException.class, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
public List<XxlJobLogDTO> log(Map<String, Object> params) {
try {
if (Boolean.FALSE.equals(redisTemplate.hasKey(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY))) {
login();
}
if (ObjectUtils.isEmpty(params)) {
return Lists.newArrayList();
}
JSONObject result = xxlJobClient.log(String.valueOf(redisTemplate.opsForValue().get(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY)), params);
if (ObjectUtils.isNotEmpty(result) && ObjectUtils.isNotEmpty(result.getString("data"))) {
return JSON.parseArray(result.getString("data"), XxlJobLogDTO.class);
}
} catch (FeignException e) {
redisTemplate.delete(CacheConstant.XXL_JOB_COOKIE_REDIS_KEY);
throw ExceptionFactory.systemException("远程操作xxl-job失败,进行重试!", e);
}
return Lists.newArrayList();
}
/**
* 重试次数达到最大后回调处理
*
* @param systemException 重试异常
*/
@Recover
public void recoverCallback(SystemException systemException) {
log.error("远程操作xxl-job异常!", systemException);
}
}
import cn.hutool.core.bean.BeanUtil;
import com.gaodun.pms.cdp.common.dto.xxljob.XxlJobInfoDTO;
import com.gaodun.pms.cdp.common.enums.MisfireStrategyEnum;
import com.gaodun.pms.cdp.common.request.xxljob.AddOrUpdateXxlJobInfoRequest;
import com.gaodun.pms.cdp.web.CdpApplication;
import feign.Response;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* xxl-job api Test
*
* @author liudong
* @date 2021/4/26 9:45
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CdpApplication.class)
public class XxlJobClientTest {
@Resource
private XxlJobClient xxlJobClient;
@Test
public void login() {
}
@Test
public void add() {
Map<String, Object> hashMap = new HashMap(2);
hashMap.put("userName", "admin");
hashMap.put("password", "123456");
Response response = xxlJobClient.login(hashMap);
AddOrUpdateXxlJobInfoRequest addOrUpdateXxlJobInfoRequest = new AddOrUpdateXxlJobInfoRequest();
addOrUpdateXxlJobInfoRequest.setJobGroup(2);
addOrUpdateXxlJobInfoRequest.setJobDesc("test");
addOrUpdateXxlJobInfoRequest.setAuthor("liudong");
addOrUpdateXxlJobInfoRequest.setAlarmEmail("");
addOrUpdateXxlJobInfoRequest.setScheduleType("CRON");
addOrUpdateXxlJobInfoRequest.setScheduleConf("0/6 * * * * ?");
addOrUpdateXxlJobInfoRequest.setGlueType("BEAN");
addOrUpdateXxlJobInfoRequest.setExecutorHandler("testHandler");
addOrUpdateXxlJobInfoRequest.setExecutorRouteStrategy("FIRST");
addOrUpdateXxlJobInfoRequest.setMisfireStrategy(MisfireStrategyEnum.DO_NOTHING.toString());
addOrUpdateXxlJobInfoRequest.setExecutorBlockStrategy("SERIAL_EXECUTION");
XxlJobInfoDTO xxlJobInfoDTO = new XxlJobInfoDTO();
BeanUtils.copyProperties(addOrUpdateXxlJobInfoRequest, xxlJobInfoDTO);
Map<String, Object> stringObjectMap = BeanUtil.beanToMap(addOrUpdateXxlJobInfoRequest);
response.headers().get("set-cookie").forEach(e -> {
if (e.contains("XXL_JOB_LOGIN_IDENTITY")) {
System.out.println(xxlJobClient.add(e, stringObjectMap).toString());
}
});
}
@Test
public void update() {
Map<String, Object> hashMap = new HashMap(2);
hashMap.put("userName", "admin");
hashMap.put("password", "123456");
Response response = xxlJobClient.login(hashMap);
AddOrUpdateXxlJobInfoRequest addOrUpdateXxlJobInfoRequest = new AddOrUpdateXxlJobInfoRequest();
addOrUpdateXxlJobInfoRequest.setId(14);
addOrUpdateXxlJobInfoRequest.setJobGroup(2);
addOrUpdateXxlJobInfoRequest.setJobDesc("update");
addOrUpdateXxlJobInfoRequest.setAuthor("liudong");
addOrUpdateXxlJobInfoRequest.setAlarmEmail("");
addOrUpdateXxlJobInfoRequest.setScheduleType("CRON");
addOrUpdateXxlJobInfoRequest.setScheduleConf("0/6 * * * * ?");
addOrUpdateXxlJobInfoRequest.setGlueType("BEAN");
addOrUpdateXxlJobInfoRequest.setExecutorHandler("testHandler");
addOrUpdateXxlJobInfoRequest.setExecutorRouteStrategy("FIRST");
addOrUpdateXxlJobInfoRequest.setMisfireStrategy(MisfireStrategyEnum.DO_NOTHING.toString());
addOrUpdateXxlJobInfoRequest.setExecutorBlockStrategy("SERIAL_EXECUTION");
XxlJobInfoDTO xxlJobInfoDTO = new XxlJobInfoDTO();
BeanUtils.copyProperties(addOrUpdateXxlJobInfoRequest, xxlJobInfoDTO);
Map<String, Object> stringObjectMap = BeanUtil.beanToMap(addOrUpdateXxlJobInfoRequest);
response.headers().get("set-cookie").forEach(e -> {
if (e.contains("XXL_JOB_LOGIN_IDENTITY")) {
System.out.println(xxlJobClient.update(e, stringObjectMap).toString());
}
});
}
@Test
public void remove() {
Map<String, Object> hashMap = new HashMap(2);
hashMap.put("userName", "admin");
hashMap.put("password", "123456");
Response response = xxlJobClient.login(hashMap);
response.headers().get("set-cookie").forEach(e -> {
if (e.contains("XXL_JOB_LOGIN_IDENTITY")) {
System.out.println(xxlJobClient.remove(e, 23).toString());
}
});
}
@Test
public void start() {
Map<String, Object> hashMap = new HashMap(2);
hashMap.put("userName", "admin");
hashMap.put("password", "123456");
Response response = xxlJobClient.login(hashMap);
response.headers().get("set-cookie").forEach(e -> {
if (e.contains("XXL_JOB_LOGIN_IDENTITY")) {
System.out.println(xxlJobClient.start(e, 22).toString());
}
});
}
@Test
public void stop() {
Map<String, Object> hashMap = new HashMap(2);
hashMap.put("userName", "admin");
hashMap.put("password", "123456");
Response response = xxlJobClient.login(hashMap);
response.headers().get("set-cookie").forEach(e -> {
if (e.contains("XXL_JOB_LOGIN_IDENTITY")) {
System.out.println(xxlJobClient.stop(e, 22).toString());
}
});
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。