多节点服务器定时任务重复处理的问题

多节点服务器定时任务重复处理的问题项目中有使用Spring定时执行任务的需求,用户可以自定义时间(半小时或整点)去生成需要的报表并发送邮件到用户自己的邮箱。项目里面提供的时间是半小时或整点去执行Spring定时任务,查询数据库中有哪些Schedule是满足要求的,然后去执行那些符合条件的任务。一切功能表现正常,但是项目部署在服务器上后,用户反映在同一时间会收到两封相同的邮件。我们检查了代码和SpringSchedule本

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

项目中有使用Spring定时执行任务的需求,用户可以自定义时间(半小时或整点)去生成需要的报表并发送邮件到用户自己的邮箱。
项目里面提供的时间是半小时或整点去执行Spring定时任务,查询数据库中有哪些Schedule是满足要求的,然后去执行那些符合条件的任务。
一切功能表现正常,但是项目部署在服务器上后,用户反映在同一时间会收到两封相同的邮件。我们检查了代码和Spring Schedule本身的机制后,发现这并不是代码层面的问题,于是我们将目光转移到了服务器上。

公司使用的服务器是Websphere,我们检查服务器的配置后发现。为了提高用户响应效率,服务器本身使用了两个节点(node)来实现负载均衡。也就是说用户的请求会随机分配到两个节点的任意一个节点上,从而达到优化的目的。但是对于Spring定时任务的这种情况,其实是脱离的负载均衡的概念,反而会导致每个节点上都会在同一时间执行相同的代码。

我们想要达成的目标是:对于一个用户任务,如果当前任务已经被某一个节点处理后,另外一个几点就不需要执行这个任务了
每个节点都是一个独立的Server,它们的JVM是相互独立的。也就是说在内存方面我们是没办法做到节点之间的相互通信。所以需要一个第三方的媒介去完成两个节点的通信。查询了一些相关的资料后,发现要么太复杂,要么代价太昂贵。所以,我们将切入点放在数据库上,因为两个节点都是连接同一个数据库,如果在处理的过程中,给数据库里的任务标记相应的标签,那么就可以变相的实现两个节点的通信。

所以,我做了如下如下尝试
1)在数据库的scheuleTask表中,添加了execute_flag字段,用来存放执行代码的节点生成的UUID
2)在代码层面,在执行任务的时候,首先生成一个UUID,然后将UUID存储在当前任务的记录上。然后再从数据库里查询当前记录的UUID,如果数据库中的UUID与当前节点生成UUID相匹配,则执行任务的具体逻辑,反之,则什么都不做处理。
伪代码如下:
[java] view plain copy

  1. String uuid = UUIDGenerator.getUUID();  
  2.   
  3. userTaskDao.markFlag(taskId, uuid);  
  4.   
  5. Thread.sleep(100);  
  6.   
  7. String existUuid = userTaskDao.getExecuteFlag(taskId);  
  8.   
  9. if(uuid.equals(existUuid)) {  
  10.   
  11.     // execute the task logic  
  12.   
  13.    ….  
  14.   
  15. }  



这么处理之后,情况有了好转。但是还是会出现某个客户有可能收到两封相同的邮件的情况。我检查了Log日志,发现某些情况下,某些任务并不是在定点时间去执行的,由于每个服务器的具体情况不一样,比如线程消费情况,在执行上述代码时会有几秒钟的时间差。从而导致了如下情况:
node1: 标记Flag-> 查询数据库中的Flag-> 发现Flag相匹配,执行用户任务
node2:………………………..获取可用线程或其他原因….->标记Flag->查询数据库中的Flag->发现Flag相匹配,执行用户任务

这样还是无法避免多个节点处理同一个用户任务的请求。针对于之前的上面的特殊情况,我们又做了一些改进,考虑到两个节点执行时相差的时间不会很多,我就定了一个粗略的阈值5min.又做了如下改动
1)在scheduleTask表中,又添加了executeTime字段,用于记录标记时的时间戳,也可以大致理解为上一次任务执行的时间戳
2)在做标记前,首先检查当前任务的上一次执行时间离当前时间超过阈值,如果超过则表明还没有其他节点执行该任务,然后为task保存标签和当前运行时间。当然如果上一次运行时间为空的情况下,也是允许标记的
3)从数据库里查询当前记录的UUID,如果数据库中的UUID与当前节点生成UUID相匹配,则执行任务的具体逻辑

伪代码如下:
[java] view plain copy

  1. String uuid = UUIDGenerator.getUUID();  
  2.   
  3.   
  4. Date stamp = new Date();  
  5.   
  6.   
  7. Task task = userTaskDao.getTask(taskId);  
  8.   
  9. if(task.getExecuteTime() == null || Math.abs(stamp.getTime() –  task.getExecuteTime().getTime())  > 300 * 1000)) {  
  10.   
  11.     userTaskDao.markFlag(taskId, uuid, stamp);  
  12.   
  13. else {  
  14.   
  15.   log.info(“task :” + taskId +” has been executed by other nodes”);  
  16.   
  17. }  
  18.   
  19. Thread.sleep(1000);  
  20.   
  21. String existUuid = userTaskDao.getExecuteFlag(taskId);  
  22.   
  23. if(uuid.equals(existUuid)) {  
  24.   
  25.     // execute the task logic  
  26.   
  27.    ….  
  28.   
  29. }  



为了处理node1,node2同时在执行标记的过程中,先完成标记的node读到是无效的数据,这里在执行读的操作前休眠1秒的时间,用来解决可能出现的Race Condition问题。

这样就达到了自己预期的效果。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/184691.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • js 判断字符串中是否包含某个字符串的方法实例

    js 判断字符串中是否包含某个字符串的方法实例String对象的方法方法一:indexOf()(推荐)varstr=”123″;console.log(str.indexOf(“3”)!=-1);//trueindexOf()方法可返回某个指定的字符串值在字符串中首次出现的位置。如果要检索的字符串值没有出现,则该方法返回-1。方法二:search()varstr=…

    2022年10月6日
    7
  • ssm/php/java/net/python中小学生视频在线网站「建议收藏」

    本系统带文档lw1万字+答辩PPT+查重如果这个题目不合适,可以去我上传的资源里面找题目,找不到的话,评论留下题目,或者站内私信我,有时间看到机会给你发系统设计.1系统体系结构中小学生视频在线网站的结构图-1所示:图-1系统结构登录系统结构图,如图-2所示:图-2登录结构图管理员结构图,如图-3所示。图-3管理员结构图.2开发流程设计系统流程的分析是通过调查系统所涉及问题的识别、可行性、可操作性、系统分析处理能力等具体环节来调节、整理系统的设计方案以确保系统能达到理

    2022年4月7日
    65
  • 给空白包签名MD5/认领应用/所有权认证

    给空白包签名MD5/认领应用/所有权认证

    2021年10月1日
    84
  • 女神相册密码忘记了,我只用Python写了20行代码

    女神相册密码忘记了,我只用Python写了20行代码​目录一、事情是这样的二、首先回顾一下女神的操作流程三、需要密码打开相册四、python脚本化处理1、基本思路2、解压命令3、解压命令参数分析4、整理7z解压命令5、关门!上python脚本五、找女神去…一、事情是这样的今早上班,公司女神小姐姐说,她去年去三亚旅游的照片打不开了好奇问了一下才知道。原来是,她把照片压缩了,而且还加了密码。但是密码不记得了,只记得是一串6位数字。话说照片压缩率也不高,而且还加密,难道是有什么可爱的小照片.

    2022年5月2日
    50
  • 计算机cpu的字母,笔记本电脑CPU型号后缀字母HQ、U、Y含义详解

    计算机cpu的字母,笔记本电脑CPU型号后缀字母HQ、U、Y含义详解我们在选购笔记本的时候,可能优先会看CPU是什么型号的,因为CPU的好坏决定了电脑运算速度的好坏,因此CPU选购十分重要。在笔记本中,不少笔记本CPU型号后缀字母可能不同,那么它们代表什么含义呢?今天蝈蝈就来给大家讲讲笔记本CPU型号后缀字母HQ、U、Y含义,希望对你有帮助!Y:超低压,性能很弱功耗很低相比低压CPU,名字中有“Y”的是性能更弱的超低压CPU。在第七代处理器之前,超低压的命名方式为…

    2022年6月16日
    78
  • db4o数据库的基本操作

    db4o数据库的基本操作对db4o(面向对象的数据库)的基本操作: 数据库的链接:privatestaticObjectContainerdb; finalstaticStringDB4OFILENAME=System.getProperty("user.dir") +"/database.db4o"; static{ db=Db4oEmbedde…

    2022年7月21日
    16

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号