项目中有使用Spring定时执行任务的需求,用户可以自定义时间(半小时或整点)去生成需要的报表并发送邮件到用户自己的邮箱。
项目里面提供的时间是半小时或整点去执行Spring定时任务,查询数据库中有哪些Schedule是满足要求的,然后去执行那些符合条件的任务。
一切功能表现正常,但是项目部署在服务器上后,用户反映在同一时间会收到两封相同的邮件。我们检查了代码和Spring Schedule本身的机制后,发现这并不是代码层面的问题,于是我们将目光转移到了服务器上。
公司使用的服务器是Websphere,我们检查服务器的配置后发现。为了提高用户响应效率,服务器本身使用了两个节点(node)来实现负载均衡。也就是说用户的请求会随机分配到两个节点的任意一个节点上,从而达到优化的目的。但是对于Spring定时任务的这种情况,其实是脱离的负载均衡的概念,反而会导致每个节点上都会在同一时间执行相同的代码。
我们想要达成的目标是:对于一个用户任务,如果当前任务已经被某一个节点处理后,另外一个几点就不需要执行这个任务了。
每个节点都是一个独立的Server,它们的JVM是相互独立的。也就是说在内存方面我们是没办法做到节点之间的相互通信。所以需要一个第三方的媒介去完成两个节点的通信。查询了一些相关的资料后,发现要么太复杂,要么代价太昂贵。所以,我们将切入点放在数据库上,因为两个节点都是连接同一个数据库,如果在处理的过程中,给数据库里的任务标记相应的标签,那么就可以变相的实现两个节点的通信。
所以,我做了如下如下尝试
1)在数据库的scheuleTask表中,添加了execute_flag字段,用来存放执行代码的节点生成的UUID
2)在代码层面,在执行任务的时候,首先生成一个UUID,然后将UUID存储在当前任务的记录上。然后再从数据库里查询当前记录的UUID,如果数据库中的UUID与当前节点生成UUID相匹配,则执行任务的具体逻辑,反之,则什么都不做处理。
伪代码如下: