场景说明:当前系统是分布式的,工程中有定时任务,要求同一时间只有一个任务出发执行,也不要都在一个部署的服务执行。
技术引用: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);
}
}
}