分布式多机集群环境下定时任务只执行一次
当我们有多个服务器,每个服务器上都有相同的定时任务代码时,比如每天凌晨定时插入数据。如果多个服务器上的定时任务都执行了会导致数据的重复;如果只让一个服务器有定时代码,部署麻烦,需要多套代码,万一该机器挂了就不可以了。一般解决办法有如下几种:数据库实现;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁;4.利用quartz集群分布式1.数据库实现(1)数据库建立一个定时表,记录
当我们有多个服务器,每个服务器上都有相同的定时任务代码时,比如每天凌晨定时插入数据。
如果多个服务器上的定时任务都执行了会导致数据的重复;
如果只让一个服务器有定时代码,部署麻烦,需要多套代码,万一该机器挂了就完犊子了。
一般解决办法有如下几种:
数据库实现;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁;4.利用quartz集群分布式
1.数据库实现
(1)数据库建立一个表,用来记录uuid(或ip)和插入时间;
(2)当多个服务器执行定时任务的时候先生成个uuid把uuid(或服务器ip)和当前时间插入表里;
(3)表中查询当天最早的插入时间那条记录返回,与uuid(或服务器自己的ip)对比;
(4)若服务器自己的uuid(或ip)与数据库查询的uuid(ip)匹配则执行,不匹配则不执行;
2.使用Shedlock库
Shedlock库可以确保你的定时任务最多同时执行一次。如果一个任务正在一个节点上执行,它会获取一个锁,以防止从另一个节点(或线程)执行相同的任务。
Shedlock库的github地址:Shedlock
示例:
2.1 对于spring或者springboot项目:
@Scheduled(cron = "0 */15 * * * *")
@SchedulerLock(name = "scheduledTaskName")
public void scheduledTask() {
// do something
}
2.2 对于没有使用框架的项目:
LockingTaskExecutor executor = new DefaultLockingTaskExecutor(lockProvider);
...
Instant lockAtMostUntil = Instant.now().plusSeconds(600);
executor.executeWithLock(runnable, new LockConfiguration("lockName", lockAtMostUntil));
3.Redis的分布式锁实现
(1) 每个服务器生成随机uuid
(2) 利用redis的Setnx命令将随机uuid作为value存入redis
(3) 获取redis的value,与生成的随机uuid对比;
(4) value与生成的随机uuid匹配则执行,不匹配则不执行;
4.Redission的分布式锁来实现(推荐)
Redisson 是 redis 官网推荐实现分布式锁的一个第三方类库,通过开启另一个服务,后台进程定时检查持有锁的线程是否继续持有锁了。
4.1 引入maven
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.19.1</version>
</dependency>
4.2 测试代码
public RedissonClient getRedissonClient() {
// 默认连接地址 127.0.0.1:6379
RedissonClient redisson = Redisson.create();
// Config config = new Config();
// config.useSingleServer().setAddress("redis://" + redisAddress); //redis服务器地址
// .setDatabase(0) //制定redis数据库编号
// .setUsername("").setPassword() // redis用户名密码
// .setConnectionMinimumIdleSize(10) //连接池最小空闲连接数
// .setConnectionPoolSize(50) //连接池最大线程数
// .setIdleConnectionTimeout(60000) //线程的超时时间
// .setConnectTimeout(6000) //客户端程序获取redis链接的超时时间
// .setTimeout(60000) //响应超时时间
// RedissonClient redisson = Redisson.create(config);
return redisson;
}
public void test() {
//获取配置
RedissonClient redissonClient = getRedissonClient();
//获取锁对象
RLock lock = redissonClient.getLock("myLock");
//加锁
lock.lock();
//尝试获取锁 。尝试获取锁的最大等待时间为20秒(超过20秒则认为获取锁失败),只锁定10秒(超过10秒自动解锁,应设置锁定时间大于业务处理的时间)
boolean res = lock.tryLock(20, 10, TimeUnit.SECONDS);
//获取锁成功才执行业务代码
if (res) {
try {
//这里编写业务逻辑代码
...
} finally {
lock.unlock();
}
}
}
参考:
Spring Scheduled Task running in clustered environment
How can you make a cluster run a task only once
集群服务器定时任务重复执行的解决方案
Redis分布式锁解析
分布式环境下定时任务如何做到只执行一次
Redisson分布式锁
Redis分布式锁-这一篇全了解(Redission实现分布式锁完美方案)
Distributed locks and synchronizers
Redis实现分布式锁
更多推荐
所有评论(0)