借助redis实现分布式定时任务锁

场景说明:当前系统是分布式的,工程中有定时任务,要求同一时间只有一个任务出发执行,也不要都在一个部署的服务执行。

技术引用:redisson 3.17.7、spring boot 2.2.5

核心代码:

配置类

package com.example.demo;

import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
public class RedissionConfig {
    private static final String RAtomicName = "genId_";
    private static Config config = new Config();
    private static RedissonClient redisson = null;

    public static void init() {
        try {
           //静态方法先加载,所以使用这种方式获取配置文件的内容
            ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class );
            String hosts = run.getBean(Environment.class).getProperty("spring.redis.cluster.nodes");
            String password = run.getBean(Environment.class).getProperty("spring.redis.password");
            String[] host = hosts.split(",");
            StringBuilder hostStr = new StringBuilder();
            for (String s : host) {
                hostStr.append("redis://" + s).append(";");
            }
            config.useClusterServers().setPassword(password)
                    .setScanInterval(200000)//设置集群状态扫描间隔
                    .setMasterConnectionPoolSize(10000)//设置对于master节点的连接池中连接数最大为10000
                    .setSlaveConnectionPoolSize(10000)//设置对于slave节点的连接池中连接数最大为500
                    .setIdleConnectionTimeout(10000)//如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒。
                    .setConnectTimeout(30000)//同任何节点建立连接时的等待超时。时间单位是毫秒。
                    .setTimeout(3000)//等待节点回复命令的时间。该时间从命令发送成功时开始计时。
                    .setRetryInterval(3000)//当与某个节点的连接断开时,等待与其重新建立连接的时间间隔。时间单位是毫秒。
                    .addNodeAddress(hostStr.toString().split(";"));
            redisson = Redisson.create(config);

            RAtomicLong atomicLong = redisson.getAtomicLong(RAtomicName);
            atomicLong.set(0);//自增设置为从0开始
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static RedissonClient getRedisson() {
        if (redisson == null) {
            RedissionConfig.init(); //初始化
        }
        return redisson;
    }
}

加锁工具类

package com.example.demo;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;


@Component
public class RedissonLock {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedissonLock.class);
    private static RedissonClient redissonClient = RedissionConfig.getRedisson();

    //加锁
    public boolean lock(String lockName,long second ) {
        RLock myLock = redissonClient.getLock(lockName);
        //lock提供带timeout参数,timeout结束强制解锁,防止死锁
//        myLock.lock(30, TimeUnit.SECONDS);
        // 1. 最常见的使用方法
        //lock.lock();
        // 2. 支持过期解锁功能,10秒以后自动解锁, 无需调用unlock方法手动解锁+
        //lock.lock(10, TimeUnit.SECONDS);
        // 3. 尝试加锁,最多等待3秒,上锁以后10秒自动解锁
        try {
            boolean res = myLock.tryLock(1, second, TimeUnit.SECONDS);
            System.out.println(res);
            return res;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.err.println("======lock======" + Thread.currentThread().getName());
        return false;
    }
    public void lock(String lockName) {
        lock(lockName , 30);
    }
    //锁是否存在
    public boolean isLock(String lockName) {
        String key = lockName;
        RLock myLock = redissonClient.getLock(key);
        return myLock.isLocked();
    }

    //解锁
    public void unLock(String lockName) {
        String key = lockName;
        RLock myLock = redissonClient.getLock(key);
        myLock.unlock();
        System.err.println("======unlock======" + Thread.currentThread().getName());
    }
}

yml配置

server:
  port: 8081
spring:
  redis:
    database: 0
    cluster:
      nodes: 192.168.56.201:6379,192.168.56.202:6379,192.168.56.203:6379
    password: 123456
    timeout: 30s
    lettuce:
      pool:
        max-wait: -1
        max-active: 300
        max-idle: 100
        min-idle: 20

测试

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

@SpringBootApplication
@EnableScheduling
public class DemoApplication {
	@Autowired
	RedissonLock lock;
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
	@Scheduled(cron = "0 40 11 * * ?")
	public void scheduled1() throws InterruptedException {
		for (int i = 0; i < 5; i++) {
			String key ="lo"+i;
			if (lock.isLock(key)){
				continue;
			}
			boolean lock = this.lock.lock(key, 3);
			if (!lock){
				continue;
			}
			System.out.println("=====>>>>>使用cron  "+System.currentTimeMillis());
			System.out.println(key);
			Thread.sleep(1000);
			this.lock.unLock(key);
		}
	}
	@Scheduled(cron = "0 42 11 * * ?")
	public void scheduled2() throws InterruptedException {
		for (int i = 0; i < 5; i++) {
			String key ="lo"+i;
			if (lock.isLock(key)){
				continue;
			}
			//判断尝试加锁是否成功
			boolean lock = this.lock.lock(key, 3);
			if (!lock){
				continue;
			}
			System.out.println("=====>>>>>使用cron  "+System.currentTimeMillis());
			System.out.println(key);
			Thread.sleep(1000);
			this.lock.unLock(key);
		}
	}
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值