【热点文章-定时计算】分布式任务调度框架xxl-job

分布式任务调度

在分布式架构下,一个服务会部署多个实例来运行业务;如果在这种分布式系统环境下运行任务调度,称为分布式任务调度。
在这里插入图片描述
分布式任务调度框架:xxl-job

xxl-job环境搭建

本机

仓库源码:xxl-job
在这里插入图片描述

  1. 初始化调度数据库
  2. 修改数据库连接信息
    在这里插入图片描述

此时启动xxl-job-admin项目,在浏览器输入http://localhost:8080/xxl-job-admin即可看到调度中心页面

docker

docker安装xxl-job

docker run -d \
-e PARAMS="--spring.datasource.url=jdbc:mysql://192.168.140.102:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
--spring.datasource.username=root \
--spring.datasource.password=123" \
-p 8888:8080 -v /tmp:/data/applogs \
--name xxl-job-admin \
--restart=always  \
xuxueli/xxl-job-admin:2.3.0

此时在浏览器输入http://192.168.140.102:8888/xxl-job-admin即可看到调度中心页面

项目集成xxl-job

  1. 导入xxl-job依赖
<dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>2.3.0</version>
</dependency>
  1. 添加配置
@Configuration
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.port}")private int port;@Beanpublic XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(adminAddresses);xxlJobSpringExecutor.setAppname(appname);xxlJobSpringExecutor.setPort(port);return xxlJobSpringExecutor;}
}

在这里插入图片描述

xxl:job:admin:addresses: http://192.168.140.102:8888/xxljob-adminexecutor:appname: xxl-job-executor-sample # 去“任务调度中心-执行器管理”里找port: 9999
  1. 任务代码
    在这里插入图片描述
@Component
public class HelloJob {@XxlJob("demoJobHandler") // 去任务调度中心里查public void helloJob() {System.out.println("简单任务执行了");}
}

任务详解

在这里插入图片描述

执行器

任务绑定的执行器,任务触发调度时,会自动发现注册成功的执行器,实现任务自动发现功能。执行器也可以方便的进行任务分组,每个任务必须绑定一个执行器。

报警邮件

任务调度失败时邮件通知的邮箱地址

调度配置

一般选用CRON表达式 在这里插入图片描述

任务配置

在这里插入图片描述

  1. 运行模式:BEAN模式(任务以JobHandler方式维护在执行器端)
  2. JobHandler:运行模式为 “BEAN模式” 时生效,对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值

路由策略

在这里插入图片描述

  • FIRST(第一个):固定选择第一个机器;

  • LAST(最后一个):固定选择最后一个机器;

  • ROUND(轮询):每个微服务轮询的去执行任务

  • RANDOM(随机):随机选择在线的机器;

  • CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上。

  • LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;

  • LEAST_RECENTLY_USED(最近最久未使用):最久未使用的机器优先被选举;

  • FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;

  • BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;

  • SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务;

分片广播

任务路由策略选择“分片广播”的情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务。

在同一个时间点,如果需要同时执行大量的任务,肯定是需要用到集群的,此时可以给每台服务器分配多个任务。
在这里插入图片描述

假设每秒10000请求,
【轮询方式】:实例A在第一秒接受了全部10000个任务并处理,而实例B空闲,第二秒则是实例B接受并处理10000个任务,A空闲
【分片广播方式】:实例A和实例B两个服务同时执行10000个任务

@Component
public class HelloJob {@XxlJob("shardingJobHandler")public void shardingJobHandler() {// 分片的参数int shardIndex = XxlJobHelper.getShardIndex(); // 当前某个分片int shardTotal = XxlJobHelper.getShardTotal(); // 总分片数// 业务逻辑List<Integer> list = getList();for(Integer i : list) {if(i % shardTotal == shardIndex) {System.out.println("当前第"+shardIndex+"分片执行了,任务项是:" + i);}}}public List getList() {ArrayList<Integer> list = new ArrayList<>();for (int i = 0; i < 10000; i++) {list.add(i);}return list;}
}

应用:热点文章定时计算

spring传统的定时任务@Scheduled,但是存在一些问题:

  1. 集群任务的重复执行问题
  2. cron表达式定义在代码中,修改不方便
  3. 定时任务失败了,无法重试,也没有统计
  4. 如果任务量过大,不能有效地分片执行

需求

在这里插入图片描述

计算热点文章分值业务代码

/*** 计算热点文章*/
@Override
public void computeHotArticle() {// 1. 查询前五天的文章数据Date dayParam = DateTime.now().minusDays(5).toDate();List<ApArticle> articleList = articleMapper.findArticleListByLast5days(dayParam);// 2. 计算文章的分值List<HotArticleVo> hotArticleVoList = computeArticleScore(articleList);// 3. 为每个频道缓存30条分值较高的文章cacheTagToRedis(hotArticleVoList);
}/*** 计算文章分值* @param articleList* @return*/
private List<HotArticleVo> computeArticleScore(List<ApArticle> articleList) {if (articleList == null || articleList.size() == 0) {return null;}List<HotArticleVo> hotArticleVoList = new ArrayList<>();for (ApArticle apArticle : articleList) {int score = computeScore(apArticle);HotArticleVo hotArticleVo = new HotArticleVo();BeanUtils.copyProperties(apArticle, hotArticleVo);hotArticleVo.setScore(score);hotArticleVoList.add(hotArticleVo);}return hotArticleVoList;
}/*** 计算文章的具体分值* @param apArticle* @return*/
private int computeScore(ApArticle apArticle) {Integer scere = 0;if(apArticle.getLikes() != null){scere += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;}if(apArticle.getViews() != null){scere += apArticle.getViews();}if(apArticle.getComment() != null){scere += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;}if(apArticle.getCollection() != null){scere += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;}return scere;
}/*** 缓存到redis中* @param hotArticleVoList*/
private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {// 1. 为每个频道缓存30条分值较高的文章ResponseResult responseResult = wemediaClient.getChannels();if(responseResult.getCode().equals(200)) {// List<WmChannel> wmChannels = (List<WmChannel>)responseResult.getData();String channelJson = JSON.toJSONString(responseResult.getData());List<WmChannel> wmChannels = JSON.parseArray(channelJson, WmChannel.class);// 检索每个频道的文章if(wmChannels != null && wmChannels.size() > 0) {for(WmChannel wmChannel : wmChannels) {// 当前频道的数据,降序排列,最多30条List<HotArticleVo> list = hotArticleVoList.stream().filter(x -> wmChannel.getId() == x.getChannelId()).limit(30) // 最多30条.sorted(Comparator.comparing(HotArticleVo::getScore).reversed())// 给文章进行排序.collect(Collectors.toList());cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + wmChannel.getId(), JSON.toJSONString(list));}}}// 2. 设置推荐的数据List<HotArticleVo> list = hotArticleVoList.stream().limit(30) // 最多30条.sorted(Comparator.comparing(HotArticleVo::getScore).reversed())// 给文章进行排序.collect(Collectors.toList());cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG, JSON.toJSONString(list));
}

设置定时任务(凌晨两点执行一次)

  1. 新增执行器
    在这里插入图片描述
  2. 新增任务
    在这里插入图片描述
  3. 引入依赖
<dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>2.3.0</version>
</dependency>
  1. 添加配置
@Configuration
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.port}")private int port;@Beanpublic XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(adminAddresses);xxlJobSpringExecutor.setAppname(appname);xxlJobSpringExecutor.setPort(port);return xxlJobSpringExecutor;}
}
xxl:job:admin:addresses: http://192.168.140.102:8888/xxl-job-adminexecutor:appname: leadnews-hotarticle-executor	port: 9999
  1. 创建任务
@Component
@Slf4j
@RequiredArgsConstructor
public class ComputeHotArticleJob {private final HotArticleService hotArticleService;@XxlJob("computeHotArticleJob")public void handle() {log.info("热文章分值计算调度任务开始执行");hotArticleService.computeHotArticle(); // 调用“计算热点文章分值业务代码”log.info("热文章分值计算调度任务执行结束");}
}

只要在查询文章列表的时候,先去判断redis中是否有数据,如果有数据,直接从redis中获取。(redis中存储的数据就是按照文章的热点分值排序后的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/70400.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式面试题 C/C++常见面试题整理_7

一.什么函数不能声明为虚函数? 常见的不能声明为虚函数的有:普通函数(非成员函数):静态成员函数;内联成员函数;构造函数;友元函数。 1.为什么C不支持普通函数为虚函数?普通函数(非成员函数)只能被overload&#xff0c;不能被override&#xff0c;声明为虚函数也没有什么意思…

【MySQL】深入了解索引背后的内部结构

目录 索引的认识&#xff1a; 作用&#xff1a; 索引的使用&#xff1a; 索引底层的数据结构&#xff1a; 哈希表 AVL树 红黑树 B树&#xff1a; B树&#xff1a; B树搜索&#xff1a; 索引的认识&#xff1a; 索引是数据库中的一个数据结构&#xff0c;用于加速查询…

数据库基础练习4(有关索引,视图完整解答)

建立需要的表 学生表 mysql> create table studnet(sno int primary key auto_increment,sname varchar(30) not null unique,ssex varchar(2) check (ssex男 or ssex女) not null ,sage int not null,sdept varchar(10) default 计算机 not null); Query OK, 0 rows affe…

最新版Node.js下载安装指定版本图文版教程(非常详细)

文字目录 1、什么是Node.js&#xff1f;2、什么是 npm&#xff1f;3、下载Node.js安装包4、详细安装步骤 1、什么是Node.js&#xff1f; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;它使 JavaScript 可以在服务器端运行。在 Node.js 出现之前&#…

Spring Boot Web 入门

目录 Spring Boot Web 是 Spring Boot 框架的一个重要模块&#xff0c;它简化了基于 Spring 的 Web 应用程序的开发过程。以下是一个 Spring Boot Web 项目的入门指南&#xff0c;涵盖了项目创建、代码编写、运行等关键步骤。 1. 项目创建 使用 Spring Initializr 使用 IDE …

深度解读 Docker Swarm

一、引言 随着业务规模的不断扩大和应用复杂度的增加,容器集群管理的需求应运而生。如何有效地管理和调度大量的容器,确保应用的高可用性、弹性伸缩和资源的合理分配,成为了亟待解决的问题。Docker Swarm 作为 Docker 官方推出的容器集群管理工具,正是在这样的背景下崭露头…

eggnog后kegg结果提取和注释

首先进入KEGG BRITE: KEGG Orthology (KO) 下载json文件 用python处理一下 import json import re import osos.chdir("C:/Users/fordata/Downloads/") with open("ko00001.json","r") as f:fj f.read()kojson json.loads(fj)with open(&qu…

qt6.8安装mysql8.0驱动

qt6.8安装mysql8.0驱动 qt6.8本身是不带mysql驱动。想要在qt里面使用mysql,还是比较麻烦的。需要自己编译驱动 首先下载qt源码&#xff0c;链接Index of /archive/qt/6.8/6.8.1/single 下载mysql对于驱动文件&#xff0c;链接是MySQL :: Download MySQL Connector/C (Archiv…

学习threejs,使用Lensflare模拟镜头眩光

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.Lensflare 二、&…

结构化表达(一):观点

目录 观点 一、观点明确 二、观点先行 三、观点先行适用范围 观点 一、观点明确 观点是指具有中心思想的一句话&#xff0c;或者称之为有灵魂的表达。 二、观点先行 如果有了明确的观点&#xff0c;并且说在前面&#xff0c;对方就会很容易记住你想表达的内容。 三、观…

Cortex-M异常处理深度解析:从HardFault到调试实战

文章目录 引言一、Cortex-M异常类型全景图二、关键寄存器:故障分析的“密码本”1. HardFault状态寄存器(HFSR, 0xE000ED2C)2. 可配置故障状态寄存器(CFSR, 0xE000ED28)3. 地址寄存器(BFAR/MMFAR)三、调试实战:从寄存器到代码定位四、自定义HardFault处理程序五、Keil与…

Retrieval-Augmented Generation,检索增强生成流程

RAG流程 用户输入接收 系统接收用户输入的查询问题或文本内容&#xff0c;例如“李白有哪些著名的作品&#xff1f;”用户输入可以通过自然语言处理&#xff08;NLP&#xff09;模型的输入端口或用户交互界面&#xff08;如聊天应用、搜索引擎输入框等&#xff09;接收。 查询…

【大模型】DeepSeek与chatGPT的区别以及自身的优势

目录 一、前言二、核心技术对比2.1 模型架构设计2.1.1 ChatGPT的Transformer架构2.1.2 DeepSeek的混合架构 2.2 训练数据体系2.2.1 ChatGPT的数据特征2.2.2 DeepSeek的数据策略 三、应用场景对比3.1 通用场景表现3.1.1 ChatGPT的强项领域3.2.2 DeepSeek的专项突破 3.3 响应效率…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(二)

接上篇&#xff1a;《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;一&#xff09;》 链接 文章目录 4.安装RabbitMQ Messaging Topology Operator 裸金属环境部署RabbitMQ部署单实例部署集群 4.安装RabbitMQ Messaging Topology Operator 使用 cer…

低至3折,百度智能云千帆宣布全面支持DeepSeek-R1/V3调用

DeepSeek-R1和 DeepSeek-V3模型已在百度智能云千帆平台上架 。 出品|产业家 新年伊始&#xff0c;百度智能云又传来新动作 。 2月3日百度智能云宣布&#xff0c; DeepSeek-R1和 DeepSeek-V3模型已在百度智能云千帆平台上架&#xff0c;同步推出超低价格方案&#xff0c;并…

STM32G0B1 ADC DMA normal

目标 ADC 5个通道&#xff0c;希望每1秒采集一遍&#xff1b; CUBEMX 配置 添加代码 #define ADC1_CHANNEL_CNT 5 //采样通道数 #define ADC1_CHANNEL_FRE 3 //单个通道采样次数&#xff0c;用来取平均值 uint16_t adc1_val_buf[ADC1_CHANNEL_CNT*ADC1_CHANNEL_FRE]; //传递…

在 Java 中使用 JDBC 连接数据库时,DriverManager 的主要作用是什么?请简要描述其工作原理。

在Java中使用JDBC&#xff08;Java Database Connectivity&#xff09;连接数据库时&#xff0c;DriverManager扮演着至关重要的角色。它主要负责以下几个方面的工作&#xff1a; 加载数据库驱动程序&#xff1a;DriverManager会根据配置或者自动发现机制加载合适的数据库驱动…

Java基础学习笔记-构造方法

### this ###### this关键字是什么&#xff1f; 可以出现在构造器方法中。 代表当前对象的地址。 谁调用了这个方法&#xff0c;this就代表谁。 this关键字是对一个对象的默认引用。每个实例方法内部都有一个this引用变量&#xff0c;指向调用这个方法的对象 因为this是在对象内…

【翻译+论文阅读】DeepSeek-R1评测:粉碎GPT-4和Claude 3.5的开源AI革命

目录 一、DeepSeek-R1 势不可挡二、DeepSeek-R1 卓越之处三、DeepSeek-R1 创新设计四、DeepSeek-R1 进化之路1. 强化学习RL代替监督微调学习SFL2. Aha Moment “啊哈”时刻3. 蒸馏版本仅采用SFT4. 未来研究计划 部分内容有拓展&#xff0c;部分内容有删除&#xff0c;与原文会有…

OpenEuler学习笔记(二十一):搭建企业AI客户服务例子

在 OpenEuler 上搭建企业 AI 客服可以按照以下步骤进行&#xff0c;以下将以使用开源的 Rasa 框架作为 AI 客服核心&#xff0c;搭配前端界面展示为例&#xff1a; 1. 系统准备 1.1 安装 OpenEuler 确保你已经安装好了 OpenEuler 操作系统&#xff0c;可以从官方网站下载镜像…