如何使用feign直接调用XXL-JOB平台API

如何使用feign直接调用XXL-JOB平台API

一、应用背景 项目中需要后端以无入侵的方式,调用调度中心API服务。然而调度中心设置了登录,调度中心API接口对cookie进行了验证,feign访问调度中心API服务时,需通过其登录验证。 二、实现原理 通过FeignClient客户端声明式调用调度中心Api服务与普通FeignClient...
一、应用背景

项目中需要后端以无入侵的方式,调用调度中心API服务。然而调度中心设置了登录,调度中心API接口对cookie进行了验证,feign访问调度中心API服务时,需通过其登录验证。

二、实现原理

通过FeignClient客户端声明式调用调度中心Api服务与普通FeignClient相比作了一下几点处理:

  • 调度中心登录Api服务返回值改为feign.Response,原始的http请求响应,方便获取cookie值;
  • 调度中心其他Api服务,新增@RequestHeader(“Cookie”) String cookie参数,传递cookie值,通过调度中心登录验证;
image.png
三、潜在问题
  1. 网络开销:
    每次调用接口如果都请求一次登录接口,难免会产生额外的网络开销,可以通过redis缓存cookie值去处理。
  2. 登录失效:
    由于调度中心cookie有效时间为2小时,需每两小时登录一次,获取新的cookie,可以通过重试机制,实现过期重新登录
    解决方案:可以参考 XxlJobComponent.java
四、代码实现
  1. 引入相关jar
org.springframework.cloudspring-cloud-starter-openfeigncom.xuxuelixxl-job-core2.3.0
  1. XxlJobClient.java
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 params);
 
    /**
     * 创建定时任务
     *
     * @param cookie cookie
     * @param params 定时任务参数
     * @return 定时任务ID
     */
    @PostMapping(value = "/xxl-job-admin/jobinfo/add", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    HttpResultForXxlJob add(@RequestHeader("Cookie") String cookie, @RequestBody Map params);
 
    /**
     * 更新定时任务
     *
     * @param cookie cookie
     * @param params 定时任务更新参数
     * @return 执行结果
     */
    @PutMapping(value = "/xxl-job-admin/jobinfo/update", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    HttpResultForXxlJob update(@RequestHeader("Cookie") String cookie, @RequestBody Map params);
 
    /**
     * 删除定时任务
     *
     * @param cookie cookie
     * @param id     定时任务更新参数
     * @return 执行结果
     */
    @DeleteMapping(value = "/xxl-job-admin/jobinfo/remove")
    HttpResultForXxlJob remove(@RequestHeader("Cookie") String cookie, @RequestParam("id") int id);
 
    /**
     * 开启任务
     *
     * @param cookie cookie
     * @param id     定时任务ID
     * @return 执行结果
     */
    @PutMapping(value = "/xxl-job-admin/jobinfo/start")
    HttpResultForXxlJob start(@RequestHeader("Cookie") String cookie, @RequestParam("id") int id);
 
    /**
     * 结束任务
     *
     * @param cookie cookie
     * @param id     定时任务ID
     * @return 执行结果
     */
    @PutMapping(value = "/xxl-job-admin/jobinfo/stop")
    HttpResultForXxlJob 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 params);
}
  1. XxlJobComponent.java
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 redisTemplate;
 
    /**
     * 登录xxl-job
     */
    public void login() {
        Map 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 log(Map 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 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 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 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 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 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 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 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());
            }
        });
    }
}

文章来源于互联网:如何使用feign直接调用XXL-JOB平台API

0

评论0

鱼翔浅底,鹰击长空,驼走大漠
没有账号? 注册  忘记密码?