美文网首页
如何使用feign直接调用XXL-JOB平台API

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

作者: 爪哇驿站 | 来源:发表于2021-05-25 17:14 被阅读0次
    一、应用背景

    项目中需要后端以无入侵的方式,调用调度中心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
    <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

    /**
     * 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);
        }
    
    }
    
    
    1. XxlJobClient.java
    /**
     * 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);
    }
    
    1. XxlJobComponent.java
    /**
     * 任务管理处理器
     *
     * @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);
        }
     
    }
    
    五、测试用例:
    /**
     * 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());
                }
            });
        }
    }
    

    相关文章

      网友评论

          本文标题:如何使用feign直接调用XXL-JOB平台API

          本文链接:https://www.haomeiwen.com/subject/qkgksltx.html