SpringBoot整合定时任务遇到的多实例问题

唠嗑部分

是这样,前几日完善了定时任务的日志记录,今日切换了服务器,多部署了一个节点,使用nginx负载均衡,但是查看日志却发现了如下情况

image-20231105153728831

那糟糕了,传说中的多实例问题出现了,今天我们就来聊聊项目实战中定时任务如何做,首先我们看如下问题

1、什么是定时任务,能帮我们解决什么实际问题?

见名知意,定时任务就是让程序指定时间去执行某段代码,例如,每日8点给女朋友发早安祝福

那么能给我们开发中解决什么问题呢?

在实际开发中,有许多需要定时任务的场景,如,每日定时去同步数据、缓存的预热、定时清理日志文件、定时统计榜单…

2、项目实战中哪些场景需要使用到定时任务?

需求一:产品经理要求实现系统的3天内热搜榜,每日0点更新数据

需求二:系统需要依赖第三方系统的数据,而且请求并发较大,第三方数据是每日更新的

需求三:系统每天都会有大量操作日志,产品经理要求只保留一个月的数据

需求四:对于系统主页数据,每日9-12点并发最大,需要定时对缓存预热

以上需求都可以用定时任务实现

3、推荐使用的定时任务组件有哪些?

Spring整合了Scheduled,轻量级而且很好用,无UI展示

xxl-Job,xxl是xxl-job的开发者大众点评的许雪里名称的拼音开头,主要用于处理分布式的定时任务,其主要由调度中心和执行器组成,有良好的UI界面。

elastic-Job,Elastic-Job是当当网推出的分布式任务调度框架,用于解决分布式任务的协调调度问题,保证任务不重复不遗漏地执行;无UI展示,需要分布式协调工具Zookeeper的支持

4、如何实现分布式定时任务,避免多实例问题?

首先我们来说说什么是多实例问题,在我们的项目开发中,我们在部署定时任务时,通常只部署一台机器,如果部署多台机器时,同一个任务会执行多次(每个机器都会执行,互不影响),那如果有一些给用户计算收益定时任务,每天定时给用户计算收益,如果部署了多台,同一个用户将重复计算多次收益,那就芭比Q了,那如果只部署一台,则会有单点故障问题,可用性无法保证

以上所说的xxl-job,elastic-Job均可以解决多实例问题,保证任务不重复不遗漏地执行

那我们使用Spring自带的Scheduled,如何避免多实例问题呢,我们可以使用redis锁来保证,具体逻辑如下

每个实例调用setnx命令插入一条数据,插入成功后返回1的实例执行job,返回0的不执行

言归正传

首先我们看下之前的代码逻辑,我这里是整合的Scheduled,自行封装的定时任务,在执行时,没有解决多实例问题

image-20231105153919796

那我们的逻辑是,在此段代码执行时加入redis锁,保证执行一次

1、redis加锁方法封装

/**
* 加锁
* @param key
* @param timeStamp
* @return
*/
public Boolean lock(String key, String timeStamp){
    if (redisTemplate.opsForValue().setIfAbsent(getKey(key), timeStamp)) {
        return true;
    }
    String currentLock = (String) redisTemplate.opsForValue().get(getKey(key));
    if (StringUtils.hasLength(currentLock) && Long.parseLong(currentLock) < System.currentTimeMillis()) {
        String preLock = (String) redisTemplate.opsForValue().getAndSet(getKey(key), timeStamp);

        if (StringUtils.hasLength(preLock) && preLock.equals(currentLock)) {
            return true;
        }
    }
    return false;
}

/**
* 解锁
* @param key
* @param timeStamp
*/
public void unLock(String key, String timeStamp){
    try {
        String currentValue = (String) redisTemplate.opsForValue().get(getKey(key));
        if (StringUtils.hasLength(currentValue) && currentValue.equals(timeStamp)) {
            redisTemplate.opsForValue().getOperations().delete(getKey(key));
        }
    } catch (Exception e) {
        log.error("解锁异常");
    }
}

2、多实例解决实现逻辑

public void run() {
    long startTime = System.currentTimeMillis();
    Map<String, Scheduled> scheduledMap = scheduledTaskService.getScheduledMap();
    ScheduledLog scheduledLog = new ScheduledLog();
    Scheduled scheduled = scheduledMap.get(beanName);
    Boolean flag = Boolean.TRUE;
    String timeStamp = String.valueOf(System.currentTimeMillis() + 300L);
    try {
        Boolean lock = redisUtil.lock(redisUtil.getCacheKey(CachePrefixContent.LOCK_PREFIX, beanName), timeStamp);
        if (lock) {
            BaseResult result = BaseResult.ok();
            scheduledLog.setTaskId(scheduled.getTaskId());
            scheduledLog.setExecuteTime(LocalDateTime.now());
            // 执行定时任务处理逻辑
            execute(result);
            if (result.resOk()) {
                scheduledLog.setExecuteStatus(Boolean.TRUE);
            } else {
                scheduledLog.setExecuteStatus(Boolean.FALSE);
            }
            scheduledLog.setExecuteDesc(result.getMsg());
            redisUtil.unLock(redisUtil.getCacheKey(CachePrefixContent.LOCK_PREFIX, beanName), timeStamp);
        } else {
            flag = Boolean.FALSE;
        }
    } catch (Exception e) {
        log.error("定时任务:{}执行失败,{}", scheduled.getTaskName(), e);
        scheduledLog.setExecuteStatus(Boolean.FALSE);
        scheduledLog.setExecuteDesc(e.getMessage());
    } finally {
        long endTime = System.currentTimeMillis();
        log.info("【{}】【】【{}ms】", "定时任务", scheduled.getTaskName(), endTime - startTime);
        if (flag) {
            completableFutureService.runAsyncTask(() -> {
                scheduledLogMapper.insert(scheduledLog);
            });
        }
    }
}

3、效果展示

每30秒两个示例只有单台节点执行成功

image-20231105162053036

结语

1、以上问题就解决了,快去给你的代码加上吧!

2、制作不易,一键三连再走吧,您的支持永远是我最大的动力!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浙大计算机学院2024届推免直博生名单

名单&#xff1a; 分析&#xff1a; 浙大计算机学院共录取推免直博生158人&#xff0c;其中计算机科学与技术专业73人&#xff0c;人工智能专业7人&#xff0c;软件工程专业21人&#xff0c;网络空间安全专业19人&#xff0c;电子信息专业31人&#xff0c;设计专业7人 欢迎关…

图文解析 Nacos 配置中心的实现

目录 一、什么是 Nacos 二、配置中心的架构 三、Nacos 使用示例 &#xff08;一&#xff09;官方代码示例 &#xff08;二&#xff09;Properties 解读 &#xff08;三&#xff09;配置项的层级设计 &#xff08;四&#xff09;获取配置 &#xff08;五&#xff09;注册…

Java快速排序算法、三路快排(Java算法和数据结构总结笔记)[7/20]

一、什么是快速排序算法 快速排序的基本思想是选择一个基准元素&#xff08;通常选择最后一个元素&#xff09;将数组分割为两部分&#xff0c;一部分小于基准元素&#xff0c;一部分大于基准元素。 然后递归地对两部分进行排序&#xff0c;直到整个数组有序。这个过程通过 par…

如何从存档服务器上完全删除PDM用户

当创建新用户时使用“PDM 登录”类型&#xff08;如下图&#xff09;&#xff0c;PDM用户名和密码会存储于存档服务器的注册表中。 存档服务器的注册表位置如下&#xff1a; HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\Applications\PDMWorks Enterprise\ArchiveServer\ConisioU…

响应式艺术作品展示前端html网站模板源码

响应式艺术作品展示网站模板是一款适合各种艺术作品在线展示的响应式网站模板下载。提示&#xff1a;本模板调用到谷歌字体库&#xff0c;可能会出现页面打开比较缓慢。 转载自 https://www.qnziyw.cn/wysc/qdmb/23778.html

C/C++轻量级并发TCP服务器框架Zinx-游戏服务器开发003:架构搭建-需求分析及TCP通信方式的实现

文章目录 1 项目总体架构2 项目需求2.1 服务器职责2.2 消息的格式和定义 3 基于Tcp连接的通信方式3.1 通道层实现GameChannel类3.1.1 TcpChannel类3.1.2 Tcp工厂类3.1.3 创建主函数&#xff0c;添加Tcp的监听套接字3.1.4 代码测试 3.2 协议层与消息类3.2.1 消息的定义3.2.2 消息…

详解JS的四种异步解决方案:回调函数、Promise、Generator、async/await

同步&异步的概念 在讲这四种异步方案之前&#xff0c;我们先来明确一下同步和异步的概念&#xff1a; 所谓同步(synchronization)&#xff0c;简单来说&#xff0c;就是顺序执行&#xff0c;指的是同一时间只能做一件事情&#xff0c;只有目前正在执行的事情做完之后&am…

Redis实战 | 使用Redis 的有序集合(Sorted Set)实现排行榜功能,和Spring Boot集成

专栏集锦&#xff0c;大佬们可以收藏以备不时之需 Spring Cloud实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏&#xff1a;https:/…

Android 扩大View可点击区域范围

有时候会遇到这种需求&#xff1a;本身控件显示在很小的范围内&#xff0c;但是要求扩大可点击的区域。根据官方文档https://developer.android.com/develop/ui/views/touch-and-input/gestures/viewgroup?hlzh-cn#delegate可以得知通过 TouchDelegate 类&#xff0c;让父视图…

rabbitMq创建交换机,以及路由键绑定队列教程

创建交换机&#xff1a; 创建队列&#xff1a; 创建路由&#xff0c;绑定到交换机&#xff1a;

手把手教你:LLama2原始权重转HF模型

LLama2是meta最新开源的语言大模型&#xff0c;训练数据集2万亿token&#xff0c;上下文长度由llama的2048扩展到4096&#xff0c;可以理解和生成更长的文本&#xff0c;包括7B、13B和70B三个模型&#xff0c;在各种基准集的测试上表现突出&#xff0c;该模型可用于研究和商业用…

AI 绘画 | Stable Diffusion 涂鸦功能与局部重绘

在 StableDiffusion图生图的面板里&#xff0c;除了图生图&#xff08;img2img&#xff09;选卡外&#xff0c;还有局部重绘(Inpaint)&#xff0c;涂鸦(Sketch)&#xff0c;涂鸦重绘(Inpaint Sketch),上传重绘蒙版&#xff08;Inpaint Uplaod&#xff09;、批量处理&#xff08…

学习伦敦银交易经验的好方法:亏损

要掌握伦敦银交易的技巧&#xff0c;除了看书学习以外&#xff0c;实践的经验也是很重要的&#xff0c;而这些实践的经验中&#xff0c;从亏损中学习会让经验会更加立体和深刻。下面我们就来讨论一下亏损这个学习伦敦银交易技巧的方法。 首先我们需要了解&#xff0c;不论是伦敦…

Android codec2 视频框架 之应用

文章目录 应用流程外部主动获取输入和输出buffer外部设置回调 内部流程 应用流程 外部主动获取输入和输出buffer 解码的调用流程&#xff0c;以android原生的一个bin来说明 android 原生代码位置&#xff1a; frameworks/av/cmds/stagefright/codec.cpp frameworks/av/cmds/st…

变压器试验VR虚拟仿真操作培训提升受训者技能水平

VR电气设备安装模拟仿真实训系统是一种利用虚拟现实技术来模拟电气设备安装过程的培训系统。它能够为学员提供一个真实、安全、高效的学习环境&#xff0c;帮助他们更好地掌握电气设备的安装技能。 华锐视点采用VR虚拟现实技术、MR混合现实技术、虚拟仿真技术、三维建模技术、人…

网络安全之CSRF漏洞原理和实战,以及CSRF漏洞防护方法

一、引言 总体来说CSRF属于一种欺骗行为&#xff0c;是一种针对网站的恶意利用&#xff0c;尽管听起来像跨站脚本&#xff08;XSS&#xff09;&#xff0c;但是与XSS非常不同&#xff0c;并且攻击方式几乎向佐。XSS利用站点内的信任用户&#xff0c;而CSRF则通过伪装来自受信任…

【MySQL数据库】 六

本文主要介绍了数据库原理中数据库索引和事务相关概念. 一.索引 在查询表的时候,最基本的方式就是遍历表,一条一条筛选 . 因此,就可以给这个表建立索引,来提高查找的速度 比如,按照id建立索引 在数据库上额外搞一个空间维护一些id 相关的信息, id:1 表的某个位置 id:2 …

Java TCP服务端多线程接收RFID网络读卡器上传数据

本示例使用设备介绍&#xff1a;WIFI/TCP/UDP/HTTP协议RFID液显网络读卡器可二次开发语音播报POE-淘宝网 (taobao.com) import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; impor…

2023年香港专才计划(输入内地人才计划)拿身份最新申请攻略!

2023年香港专才计划&#xff08;输入内地人才计划&#xff09;拿身份最新申请攻略&#xff01; 近年来&#xff0c;香港受持续的人口老龄化等多因素影响&#xff0c;2022年香港人口总计减少了约12.17万人&#xff0c;跌幅1.6%&#xff0c;其中净移出人数约9.5万人。在此背景下&…

通过创建自定义标签来扩展HTML

使用HTML时&#xff0c;例如&#xff0c;使用<b>标记显示粗体文本。 如果需要列表&#xff0c;则对每个列表项使用<ul>标记及其子标记<li> 。 标签由浏览器解释&#xff0c;并与CSS一起确定网页内容的显示方式以及部分内容的行为。 有时&#xff0c;仅使用一…