spring boot使用Redis分布式锁实现分布式环境下定时任务的管理

我们有很多时候都需要一些定时任务的辅助,大多数情况,定时任务都可能是写到一个服务节点。但是可能存在以下情况:

  •      但是如果业务逻辑过于复杂的话,不好维护。
  •      如果服务节点挂了,那么所有的定时任务都不会执行了。

        如果在微服务的体系里面,我个人感觉定时任务还是跟着服务本身走维护要方便,而且对于业务开发人员也比较方便。而且通常的情况下我们的服务都是采用分布式多节点的方式进行部署的,所以也不会出现服务节点挂了定时任务不能执行的情况。但是随之而来也带来一个情况,就是怎么能保证每次只有一个服务节点执行定时任务,不出现并发执行的情况呢。

        这里我采用的redis的分布式锁的实现方式,而具体的实现方式是redisson。

         废话不多说,直接上代码,引入POM文件:

      <!-- redission -->
      <dependency>
			<groupId>org.redisson</groupId>
			<artifactId>redisson</artifactId>
			<version>3.8.0</version>
	  </dependency>

配置redissonclent,这里采用的单机模式,个人建议采用集群或者哨兵模式的。

@Configuration
public class RedissonConf {
	/**
	 * redis连接密码
	 */
	@Value("${spring.redis.password}")
	private String pwssword;
	/**
	 * redis主机
	 */
	@Value("${spring.redis.host}")
	private String host;
	/**
	 * redis端口
	 */
	@Value("${spring.redis.port}")
	private String port;
	/**
     * 单机模式自动装配,生成环境建议使用哨兵(可以理解成主从的升级版本)或者集群
     * @return
     */
    @Bean
    RedissonClient redissonSingle() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress(new StringBuffer("redis://").append(host).append(":").append(port).toString())
                .setDatabase(0)
                .setPassword(pwssword);
        return Redisson.create(config);
    }
}

配置lock的接口

public interface DistributedLocker {
	/**
	 * 加锁
	 * @param lockKey
	 * @return
	 */
	public RLock lock(String lockKey);
	/**
	 * 加锁并设置锁过期时间
	 * @param lockKey
	 * @param timeout
	 * @return
	 */
	public RLock lock(String lockKey, int timeout);
	/**
	 * 加锁并设置锁过期时间,并指定时间格式
	 * @param lockKey
	 * @param unit
	 * @param timeout
	 * @return
	 */
	public RLock lock(String lockKey, TimeUnit unit, int timeout);
	/**
	 * tryLock(),马上返回,拿到lock就返回true,不然返回false。
     * 带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false.
	 * @param lockKey
	 * @param unit
	 * @param waitTime
	 * @param leaseTime
	 * @return
	 */
	public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);
	/**
	 * 解锁
	 * @param lockKey
	 */
	public void unlock(String lockKey);
	/**
	 * 解锁
	 * @param lock
	 */
	public void unlock(RLock lock);
}

配置lock的实现类:

@Component
public class RedissonDistributedLocker implements DistributedLocker{
	/**
	 * 注入redission累
	 */
	@Autowired
	private RedissonClient redissonClient;
	
	@Override
	public RLock lock(String lockKey) {
		// TODO Auto-generated method stub
		RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
	}

	@Override
	public RLock lock(String lockKey, int timeout) {
		// TODO Auto-generated method stub
		RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, TimeUnit.SECONDS);
        return lock;
	}

	@Override
	public RLock lock(String lockKey, TimeUnit unit, int timeout) {
		// TODO Auto-generated method stub
		RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
	}

	@Override
	public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
		// TODO Auto-generated method stub
		RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
	}

	@Override
	public void unlock(String lockKey) {
		// TODO Auto-generated method stub
		RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
	}

	@Override
	public void unlock(RLock lock) {
		// TODO Auto-generated method stub
		lock.unlock();
	}

}

修改定时任务:

@Component
public class ScheduledTask {
	/**
	 * 日志配置
	 */
	private static final Logger logger = LoggerFactory.getLogger(ScheduledTask.class);
	/**
	 * 注入业务层
	 */
	@Autowired
	private CTIService ctiService;
	/**
	 * 注入分布式锁	
	 */
	@Autowired
	private DistributedLocker distributedLocker;
	/**
	 * 批量修改的key
	 */
	public static final String BETCH_SYS_RECORDS = "Redisson_UPDATEBETCHRECORDS";
	/**
	 * 同步文件的key
	 */
	public static final String FILE_SYS="Redisson_FILESYS";
	/**
	 * 同步通话记录
	 * 每天凌晨1点执行
	 */
	@Scheduled(cron = "0 0 1 * * ?")    
    public void updateBetchRecords() {
        logger.info("我每天凌晨1点开始执行");
        boolean flag = false;
        try {
        	//若任务执行时间过短,则有可能在等锁的过程中2个服务任务都会获取到锁,这与实际需要的功能不一致,故需要将waitTime设置为0
			flag = distributedLocker.tryLock(BETCH_SYS_RECORDS, TimeUnit.SECONDS, 0, 5);
			if(flag) {
				ctiService.synchronousTask();
			}else {
				logger.info("Redisson分布式锁没有获取到锁:{},ThreadName :{}",BETCH_SYS_RECORDS,Thread.currentThread().getName());
			}
		} catch (Exception e) {
			logger.info("定时任务执行失败,分布式锁异常"+e);
		}finally {
			if(flag) {//释放锁
				distributedLocker.unlock(BETCH_SYS_RECORDS);
				logger.info("锁已释放");
			}
		}
    }
	/**
	 * 同步录音文件
	 * 每天凌晨2点执行
	 */
	@Scheduled(cron = "0 0 2 * * ?")    
    public void updateRecordFile() {
        logger.info("我每天凌晨2点开始执行");
        try {
        	//若任务执行时间过短,则有可能在等锁的过程中2个服务任务都会获取到锁,这与实际需要的功能不一致,故需要将waitTime设置为0
			boolean flag = distributedLocker.tryLock(FILE_SYS, TimeUnit.SECONDS, 0, 5);
			if(flag) {
				ctiService.synchronousRecordFile();
			}else {
				logger.info("Redisson分布式锁没有获取到锁:{},ThreadName :{}",FILE_SYS,Thread.currentThread().getName());
			}
		} catch (Exception e) {
			logger.info("定时任务执行失败,分布式锁异常"+e);
		}finally {
			distributedLocker.unlock(FILE_SYS);
		}
    }

}

到这里配置完成,修改任务的时间,分不同的端口启动项目。可以看到结果

猜你喜欢

转载自blog.csdn.net/zhuwei_clark/article/details/83655479