xxl-job+工厂模式+token实现拉取(三)

  • 目录
    • 前言
    • 整体设计
      • xxl-job说明
      • 表设计
      • 工厂模式设计
    • 码上有戏
      • 核心代码
      • 测试
      • 简单说明
      • 源码地址

目录

前言

通过前面总结我们知道,已经有四种定时任务的创建方法。但是他们都一定的局限性。而开源的xxl-job是一个分布式任务调度平台,他不仅集成简单,开箱即用。而且内置了许多强大的功能,所以是一个很优秀的job集成方案。

整体设计

本demo将承接jwt+shiro+模板模式实现推送作为数据推送端,通过xxl-job控制台管理各个任务,客户端实现我们的拉取业务

xxl-job说明

xxl-job的说明可参考其详细的文档说明xxl-job,而这里只需要下载源码导入,然后运行xxl-job-admin(后台管理),而其执行器里集成我们的拉取业务
这里选择2.3.1版本进行下载和开发
并且只保留如图所示的模块

1 初始化tables_xxl_job.sql
2 修改xxl-job-admin中application.properties中datasource和email
3 启动xxl-job-admin

浏览器输入localhost:8080/xxl-job-admin
用户名-密码 admin-123456
可看到如下界面,即成功

接下来就是xxl-job-executor-sample-springboot执行器里执行我们的拉取业务,首先从标的设计开始

表设计

核心表有两个,首先需要有一个保存最新的token信息表,每次拉取信息都过携带token,其次有一个记录业务接口的信息,用于标记每个业务接口
sys_task_token(token信息表)

CREATE TABLE `sys_task_token` (`id` int(11) NOT NULL COMMENT '主键',`token` varchar(50) DEFAULT NULL COMMENT '接口验证密钥',`date_time` datetime DEFAULT NULL COMMENT '获取token时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

sys_task_datetime(接口配置表)

CREATE TABLE `sys_task_datetime` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',`page_index` int(11) DEFAULT NULL COMMENT '页码',`api_name` varchar(50) DEFAULT NULL COMMENT '接口名称',`execute_time` datetime DEFAULT NULL COMMENT '创建时间',`after_min` int(11) DEFAULT NULL COMMENT '接口提前量',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;-- ----------------------------
-- Records of sys_task_datetime
-- ----------------------------
INSERT INTO `sys_task_datetime` VALUES ('1', '1', 'token', '2021-04-16 14:04:13', null);
INSERT INTO `sys_task_datetime` VALUES ('2', '1', 'leesin', '2021-04-14 11:17:03', null);
INSERT INTO `sys_task_datetime` VALUES ('3', '1', 'yurnero', '2020-08-18 16:07:01', null);

这里配置了三个接口,分别对应之前的token,盲僧接口,剑圣接口推送

工厂模式设计


以上是简化工厂模式uml图,事实上我们只需要用一个枚举值去控制每个具体xxApiRequest服务,在系统初始化向工厂里注入改组件即可,而实际调用时只需一行代码创建即可

 AbstractApiRequest apiRequest = factory.create(TaskEnumUtils.taskDatetimeType.LEESIN.key);apiRequest.excute();

事实上就可以获得具体子类,这也是oop充满魅力的地方

码上有戏

如图,为项目代码结构

核心代码

使用工厂模式就是为了防止模板发生变化,从而扩展多个产品线或者一个产品线下多个产品
首先是系统
注入服务

@Configuration
public class SystemConfig {@Resourceprivate LeesinApiRequest leesinApiRequest;@Resourceprivate YurneroApiRequest yurneroApiRequest;@Beanpublic ApiRequestFactory createFactory() {ApiRequestFactory factory = new ApiRequestFactory();Map<String, AbstractApiRequest> serviceMap = new HashMap<>(6);serviceMap.put(TaskEnumUtils.taskDatetimeType.LEESIN.key, leesinApiRequest);serviceMap.put(TaskEnumUtils.taskDatetimeType.YURNERO.key, yurneroApiRequest);factory.setServiceMap(serviceMap);return factory;}
}

如果有需要,可以扩展注入服务到工厂里的serverMap中

枚举核心配置

public class TaskEnumUtils {public enum taskDatetimeType {TOKEN("token"), LEESIN("leesin"), YURNERO("yurnero");public String key;private taskDatetimeType(String key) {this.key = key;}}public static EnumMap taskDatetimeTypeEnum = new EnumMap(taskDatetimeType.class) {{put(taskDatetimeType.TOKEN, "token");put(taskDatetimeType.LEESIN, "盲僧接口");put(taskDatetimeType.YURNERO, "剑圣接口");}};public enum taskApiUrlType {LEESIN("leesin"), YURNERO("yurnero");public String key;private taskApiUrlType(String key) {this.key = key;}public static String getValueByKey(String key) {for (taskApiUrlType item : values()) {if (item.key.equals(key)) {return (String) taskApiUrlTypeEnum.get(item);}}return null;}}public static EnumMap taskApiUrlTypeEnum = new EnumMap(taskApiUrlType.class) {{put(taskApiUrlType.LEESIN, "getinformation/leesinlist");put(taskApiUrlType.YURNERO, "getinformation/yurnerolist");}};
}

主要控制接口的相对url和服务的key

ResponseModel作为顶层返回类,通过向下转型可以获取任意子类类型,并且在模板中声明为抽象,交给子类

public abstract class AbstractApiRequest {@Resourceprivate RestTemplate restTemplate;@Resourceprivate ISysTaskTokenService iTskTokenService;@Resourceprivate ApiTokenRequest tokenRequest;@Resourceprivate ISysTaskDatetimeService taskDatetimeService;protected volatile String param;/*** 一次性插入数据的数量*/protected final int limit = 50;@Retryable(value = {RemoteAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 5000L, multiplier = 1))public void excute() throws IOException {//1 获取tokenString token = iTskTokenService.getToken().getToken();if (token == null || "".equals(token)) {XxlJobLogger.log("token为空!");tokenRequest.excute();}//2 获取任务执行配置String apiName = initApiName();SysTaskDatetime tskDateTime = taskDatetimeService.getByApiName(apiName);if (tskDateTime == null) {XxlJobLogger.log("接口【" + apiName + "】未配置或不存在");return;}//执行时间String executetime = DateUtil.format(tskDateTime.getExecuteTime(), "yyyy-MM-dd'T'HH:mm:ss");//当前时间String currenttime = DateUtil.format(new Date(), "yyyy-MM-dd'T'HH:mm:ss");//读取的首页数int pageIndex = tskDateTime.getPageIndex();//读取的下一页int nextPage = pageIndex + 1;// 是否有异常boolean isE = false;while (pageIndex <= nextPage) {ResponseEntity<? extends ResponseModel> responseEntity = null;Map<String, Object> map = getStringObjectMap(executetime, currenttime, pageIndex);Map<String, Object> requestExtMap = getRequestExtMap();if (null != requestExtMap) {map.putAll(requestExtMap);}String relativeApiUrl = initRelativeApiUrl();Class<? extends ResponseModel> resultMappingClass = initMappingClass();try {responseEntity = restTemplate.postForEntity(ApiParam.API_BASE_URL +ApiRequestUrlUtil.getRequestUrl(relativeApiUrl, token), ApiRequestUrlUtil.getHttpEntity(map, apiName), resultMappingClass);if (null != responseEntity.getBody()) {int result = responseEntity.getBody().getResult();if (result == 1) {if (responseEntity.getBody().getPagesize() > 0) {insertOrUpdate(responseEntity.getBody());pageIndex++;if (responseEntity.getBody().getNextpage() == 0 || responseEntity.getBody().getNextpage() == null) {nextPage = 0;break;} else {nextPage = responseEntity.getBody().getNextpage();}} else {pageIndex = 1;break;}} else {XxlJobLogger.log("任务名称:{}获取数据异常,异常信息为{}", apiName, responseEntity.getBody().getMsg());break;}} else {pageIndex = 1;break;}} catch (RestClientException e) {isE = true;XxlJobLogger.log("接口远程异常,{}", e.getMessage());String exceptionMessage = "调用任务接口【" + apiName + "】异常: " + e.getMessage() + e.getStackTrace().toString();throw new RemoteAccessException(exceptionMessage);} catch (Exception e) {nextPage = 0;isE = true;XxlJobLogger.log("接口异常,{}", e.getMessage());tskDateTime.setPageIndex(pageIndex);taskDatetimeService.updateById(tskDateTime);}}//3 执行完成后,将页码初始化,并保存当前时间if (!isE) {tskDateTime.setPageIndex(1);tskDateTime.setExecuteTime(new Date());taskDatetimeService.updateById(tskDateTime);}}@Recoverpublic void recover(RetryException e) {XxlJobLogger.log("recovery,{}", e.getMessage());}public abstract String initApiName();public abstract String initRelativeApiUrl();public abstract Class<? extends ResponseModel> initMappingClass();public abstract void insertOrUpdate(ResponseModel entity);private Map<String, Object> getStringObjectMap(String executetime, String currenttime, int pageIndex) {Map<String, Object> map = new HashMap<>(3);map.put("pageindex", pageIndex);map.put("starttime", executetime);map.put("endtime", currenttime);return map;}public abstract Map<String, Object> getRequestExtMap();public void setParam(String param) {this.param = param;}
}

通过抽象拉取业务的模板方法,将可抽象的方法交由子类去完成,比如initApiName(),initRelativeApiUrl(),initMappingClass()。当然改模板方法可以按需再扩展

具体工厂创建

public class ApiRequestFactory extends Factory {/*** 保存注入的所有ApiXXXRequest*/private Map<String, AbstractApiRequest> serviceMap;@Overrideprotected AbstractApiRequest createApiRequest(String apiName) {if (StringUtils.isEmpty(apiName) || null == this.serviceMap) {XxlJobLogger.log("初始化AbstractApiRequest失败");return null;}return serviceMap.get(apiName);}public void setServiceMap(Map<String, AbstractApiRequest> serviceMap) {this.serviceMap = serviceMap;}
}

而具体的工厂最核心的就是将注入的service按照key去标识,我们只需要声明如下形式就可以调用相应的xxApiRequest,并执行模板方法

 AbstractApiRequest apiRequest = factory.create(TaskEnumUtils.taskDatetimeType.LEESIN.key);
apiRequest.excute();

测试

我们需要先启动jwt+shiro+模板模式实现推送(二)中的代码作为推送端,启动xxljob的admin和客户端。然后通过bean模式注册客户端的bean,如下图所示

可分别执行上述盲僧和剑圣接口,即可执行客户端通过 @XxlJob声明的方法,如 @XxlJob(“leesinJobHandler”)

简单说明

当然这里只是一个demo,实际中我们可以扩展许多功能,比如多条产品线的不同业务拉取,同一个产品线的不同产品拉取等

源码地址

友情提示,需先下载推送端,在下载本demo代码github地址

数据拉取之xxl-job+工厂模式+token实现拉取(三)相关推荐

  1. 创维linux进入工厂模式,彩色电视机进入工厂模式后数据调乱了,如何恢复默认?-创维电视8TTN工厂模式数据怎么调...

    彩色电视机进入工厂模式后数据调乱了,如何恢复默认? 创维彩电进入与退出工厂模式方法的汇总 一. D系列 5D01机芯: 进入: 在遥控器屏显键的正下方,加装一个按键(SERVICE键),按该键即可进入 ...

  2. 策略模式与简单工厂模式区别(转)

    最近一直在抽时间研究设计模式,之前对设计模式也有一定的了解,但是都没有平心静气的去研究过,只是了解了一些皮毛,最近打算再深入研究一下,重新打开了设计模式的数据,对之前的疑问一个个的刨根问底,今天看了简 ...

  3. 智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)

    智能家居项目开发 一.智能家居功能细节拆分 控制区: 外设区: 面向对象类和对象的概念 结构体新玩法 二.工厂模式 1. 工厂模式的概念 2. 工厂模式的实现 3. 工厂模式使用及功能验证 三.智能家 ...

  4. 面向对象编程设计模式--简单工厂模式讲解(历史上最简单明白的例子)

    工作之余,在看资料过程中发现一个极易理解的简单工厂模式的例子,自己亲自试练一番,感觉对这个设计模式不熟悉的朋友, 一看马上就知道是什么回事了. 简单工厂模式根据提供给它的数据,返回几个可能类中的一个类 ...

  5. C++设计模式之 简单工厂模式讲解(历史上最简单明白的例子)

    工作之余,在看资料过程中发现一个极易理解的简单工厂模式的例子,自己亲自试练一番,感觉对这个设计模式不熟悉的朋友, 一看马上就知道是什么回事了. 简单工厂模式根据提供给它的数据,返回几个可能类中的一个类 ...

  6. 【大话设计模式】——简单工厂模式

    一.概念 简单工厂模式(Simple Factory Pattern)属于创建型模式,又叫做静态工厂方法模式(Static FactoryMethod Pattern),可是不属于23GOF设计模式之 ...

  7. 工厂模式和策略模式区别

    先上代码: 定义一个抽象类 //抽象类 abstract class AbsClass {//抽象方法:提供一些列的算法操作public abstract void acceptCash(string ...

  8. 创维linux进入工厂模式,创维智能电视如何打开工厂模式(各个型号汇总)!

    原标题:创维智能电视如何打开工厂模式(各个型号汇总)! 创维电视进入工厂模式汇总: 1.8H11/8H06 复位可解决故障:1.有雪花噪点,收不到台 (最为常见)2.偏色, 图像不良 .进工厂按键的& ...

  9. 工厂模式理解了没有?

    2019独角兽企业重金招聘Python工程师标准>>> 前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 昨天写了单例模 ...

最新文章

  1. 大数据岗位必知必会的53个Java基础
  2. (六)观察者模式详解(包含观察者模式JDK的漏洞以及事件驱动模型)决了当时的问题,那时LZ接触JAVA刚几个月,比葫芦画瓢的用了观察者模式。...
  3. 设置***遇到一个小问题
  4. 无标题窗体的移动及其简单美化
  5. apt-get 与 yum的区别 (转)
  6. springboot中使用lua脚本+aop作限流访问案例代码
  7. Linux 命令简单介绍第一课笔记
  8. OD使用教程20 - 调试篇20
  9. 理解javascript
  10. 土豆 android 缓存路径,#土豆记事#教你开发Android App之 —— Hello Android
  11. 怎么看公司财务报表?
  12. 如何用PS的量度标尺工具调整图片
  13. 神经元模型图手工制作,神经元模型图手工模型
  14. QNAP 威联通 NAS的个人使用经验 篇三:#剁主计划-西安# 时隔3年,NAS使用须知
  15. mysql性能调优面试题_面试题大全-mysql性能优化方案
  16. 计算机车牌识别的步骤,车牌识别流程图
  17. 【NLP】从WE、ELMo、GPT到Bert模型—自然语言处理中的预训练技术发展史
  18. 常见的夜间经济项目有哪些?
  19. ATECC508A芯片开发笔记(九):加密读写508芯片数据的流程及相应设置
  20. springboot毕设项目社区健康服务系统h9bpy(java+VUE+Mybatis+Maven+Mysql)

热门文章

  1. sql2000之不足数自动补位、补零,左侧、右侧、中间 谢子圣 |2016-04-18 |3.8分(高于91.8%的文档)|414|11 |简介 |举报 手机打开...
  2. Gartner 2020 战略技术趋势- Hyper 自动化
  3. 如何建设数字孪生灌区三维可视化平台
  4. 砍竹子——二分,模拟
  5. 微服务 API 网关架构演进 Spring Cloud Gateway ShenYu APISIX
  6. 产品读书笔记 | 赢在用户
  7. 服务器重启进不了系统
  8. 用户输入0-9数字,分别输出零 壹 贰 叁 肆 伍 陆 柒 捌 玖
  9. 专访系统防U盘复制软件创作人范磊:有创新软件才有魅力
  10. 个人计算机采用risc处理器,计算机三级PC技术临考模拟题及答案详解