先说一下我们的系统,

在65和66上分别部署有weblogic节点,共计四个,在项目中我们的定时器会隔一段时间就从其它的五个系统中取数据,这时就出现了问题,本来取一次数据就可以的,现在重复执行了三次,同时还造成了对方服务器的压力。

现在说一下我们项目怎么解决的这个问题:

1、首先想到的是在定时执行任务这个文件中添加ip地址,固定成某一台能执行,但我们一台服务器上有两个节点,方案不可行。

2、后来想到使用redis来保存一个状态位,是1表示可以执行,0表示不可以执行,但要是一个节点get到的是1,在它设置此status为0的时候,另一个节点提前一点点get到的也是1,这个时候这两个节点就都会执行,随后想到可不可以使用redis的事务加watch关键字,细想之后redis的事务是一组命名打包执行,中间不再执行其它操作,也实现不了。

3、既然redis的事务不可以,那数据库的应该可以吧。从而最终找到了现在的解决方案。

能解决的问题:

1、多个节点只有一个节点执行任务

2、即使某个正在执行的节点挂掉了,没有来的及修改状态位,也不影响定时器的执行。

下面是数据库表的设计:

这个表是用来表示当前查询的任务是否可以执行,通过type字段可以将多个不同类型的定时器进行区分,从而实现表的共用。

此表中的status字段表示是否可以执行定时任务。

Datetime字段用来记录当前任务的执行时间。

Intervallen这个字段是我们规定一段时间间隔。

代码示例如下:

------------------------------------------

这个是具体的查询是否可以执行任务的逻辑。

下面是具体的sql

这里的for update很重要。

下面针对上面的步骤说明一下:

1、当定时器执行的时候会执行running = sysScheduleStatusService.getAndSetScheduleStatus(parameter);

这个方法,running表示是否可以执行,true表示可以执行。

2、在这个方法中,会先执行SysScheduleStatus scheduleStatus = sysScheduleStatusDao.getScheduleStatus(parameter);这个方法,此方法会调用id为getScheduleStatus的sql语句,这个语句中的for update 关键字会对查询出来的记录加锁。即:

对这一条记录加锁(type为1表示某个类型的定时器)。
然后根据取出来的数据判断status是否为1,为1表示可以执行此定时器,随即调用update方法,将数据库的此字段改为0(不可以执行),提交事务,返回true。由于for update的存在,当某个线程执行这个查询语句的时候,已经对此条记录加锁,别的线程再去查询的时候就会处于等待状态,直到这个线程的事务提交,然后才可以查询,此时查询到的status为0,不可以执行,返回false。
在定时器方法的最后我们要更新表的status为1,以便下一次能顺利执行。这个update的方法我们一定要放到finally中,否则出现异常就有可能这个状态位一直为0了(其实为0也可以解决的);
我们通过status是否为1来判断是否可以执行,但要是节点在执行定时器的时候突然宕机或者其它原因,没有及时的将status设置成1,这个时候我们的定时器就永远走不了了。在这里就使用到了datetime字段和intervallen字段了。
逻辑:当status为0时,我们继续向下走,获取当前时间,当当前时间大于datetime+intervallen时我们就认为某个执行定时器的节点不是宕机就是出现其它异常导致未能及时的将status字段设置成1。这时我们照样返回true,同时更新一下对应type的status和datetime字段。这样即使节点挂掉,也不影响下一次定时任务的执行(由其它节点执行)。
在我这个代码里面我是直接在数据库里面进行了判断,isovertime表示是否超时,为1表示超时我们返回true,执行任务。(这样考虑是因为我们用sysdate来和datetime比较是比较好的,如果我们拿到java代码里面的话,有可能java运行的服务器的时间和数据库的时间不同步)
 
优化:由于我们的定时任务执行的时间比较长,其实还可以将查询出来的list分成三四个list,然后让spring的线程池threadPoolTaskExecutor来执行,从而减少执行时间。
 

要说明一点:

Service 中的这个getAndSetScheduleStatus()方法一定要开启事务。我在做这个的时候发现我们项目中的事务配置的不太对,从而这个方法没有开启事务,当多个节点执行时就出现因为for update而一直等待的情况。下图是事务的配置:


https://zhidao.baidu.com/question/510837808.html
 
 

多台或者集群环境下如何保证spring定时器只执行一个的更多相关文章

  1. Ubuntu14(64位) 集群环境下安装Hadoop2.4

    经过前边的积累,今天最终实现了集群环境下部署Hadoop.并成功执行了官方的样例. 工作例如以下: 两台机器: NameNode:上网小本,3G内存.机器名:YP-X100e,IP:192.168.1 ...

  2. CAS服务器集群和客户端集群环境下的单点登录和单点注销解决方案

    CAS的集群环境,包括CAS的客户应用是集群环境,以及CAS服务本身是集群环境这两种情况.在集群环境下使用CAS,要解决两个问题,一是单点退出(注销)时,CAS如何将退出请求正确转发到用户sessio ...

  3. 分布式集群环境下,如何实现session共享一(应用场景)

    在web应用中,由于http的请求响应式,无状态.要记录用户相关的状态信息,比如电商网站的购物车,比如用户是否登录等,都需要使用session.我们知道session是由servlet容器创建和管理, ...

  4. 分布式集群环境下,如何实现session共享四(部署项目测试)

    这是分布式集群环境下,如何实现session共享系列的第四篇.在上一篇:分布式集群环境下,如何实现session共享三(环境搭建)中,已经准备好了相关的环境:tomcat.nginx.redis.本篇 ...

  5. quartz在集群环境下的最终解决方案

    在集群环境下,大家会碰到一直困扰的问题,即多个 APP 下如何用 quartz 协调处理自动化 JOB . 大家想象一下,现在有 A , B , C3 台机器同时作为集群服务器对外统一提供 SERVI ...

  6. 集群环境下,Session管理的几种手段

    集群环境下,Session管理的几种手段 1.Session复制 缺点:集群服务器间需要大量的通信进行Session复制,占用服务器和网络的大量资源. 由于所有用户的Session信息在每台服务器上都 ...

  7. 集群环境下的Session管理

    1. 集群环境下的管理HTTPSSession所遇到的问题 一台服务器对应这个一个session对象,无法在另外一个服务器互通 解决方法: 1. Session 的 Replication(复制)将当 ...

  8. 在Hadoop1.2.1分布式集群环境下安装hive0.12

    在Hadoop1.2.1分布式集群环境下安装hive0.12 ● 前言: 1. 大家最好通读一遍过后,在理解的基础上再按照步骤搭建. 2. 之前写过两篇<<在VMware下安装Ubuntu ...

  9. 在tomcat集群环境下redis实现分布式锁

    上篇介绍了redis在集群环境下如何解决session共享的问题.今天来讲一下如何解决分布式锁的问题 什么是分布式锁? 分布式锁就是在多个服务器中,都来争夺某一资源.这时候我们肯定需要一把锁是不是 , ...

随机推荐

  1. Android开发学习之路--网络编程之xml、json

    一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...

  2. Android开发学习之路--数据持久化之初体验

    上班第一天,虽然工作上处于酱油模式,但是学习上依旧不能拉下,接着学习android开发吧,这里学习数据持久化的 知识. 其实数据持久化就是数据可以保存起来,一般我们保存数据都是以文件,或者数据库的形式 ...

  3. UNIX环境高级编程——线程和信号

    每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的.这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变.这样如果一 ...

  4. A*寻路算法入门(七)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  5. 取KindEditor中的textarea的值区不到的解决方案,固定kindEditor的高度

     可以通过下面的方式取到textarea的值 var content = $(document.getElementsByTagName('iframe')[0].contentWindow.do ...

  6. React Native控件之Listview

    ListView组件用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同. ListView更适于长列表数据,且元素个数可以增删.和ScrollView不同的是,ListView并不立即渲染 ...

  7. C++ Primer 有感(管理类的指针成员)

    C++类的指针成员与其他成员有所不同,指针成员指向一个内存地址,该地址的内存需要我没管理. 我现在分析一下为什么要管理指针成员. 有如下Student类,Student.h如下: [cpp] view ...

  8. spring2.5与hibernate3升级后的bug

    手头有一个项目,使用的是struts2 hibernate3 spring2.5 是之前的老项目了,spring与hibernate的版本都比较低 自己看了最新的spring4与hibernate4, ...

  9. 【shell脚本练习】网卡信息和简单日志分析

    题目 1.写一个脚本getinterface.sh,脚本可以接受参数(i,I,a),完成以下任务: (1)使用以下形式:getinterface.sh [-i interface|-I IP|-a] ...

  10. log4j的一些配置

    a). 新建Java Project>>新建package>>新建java类: b). import jar包(一个就够),这里我用的是log4j-1.2.14.jar, c) ...