SpringBoot集成redisson分布式锁

时间:2021-05-08 17:25:30

官方文档:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

20180226更新:增加tryLock方法,建议后面去掉DistributedLocker接口和其实现类,直接在RedissLockUtil中注入RedissonClient实现类(简单但会丢失接口带来的灵活性)。

1、引用redisson的pom

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency>

2、定义Lock的接口定义类

import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;

public interface DistributedLocker {

RLock lock(String lockKey);

RLock lock(String lockKey,
int timeout);

RLock lock(String lockKey, TimeUnit unit,
int timeout);

boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);

void unlock(String lockKey);

void unlock(RLock lock);
}

 

3、Lock接口实现类

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

public class RedissonDistributedLocker implements DistributedLocker {

private RedissonClient redissonClient;

@Override
public RLock lock(String lockKey) {
RLock lock
= redissonClient.getLock(lockKey);
lock.lock();
return lock;
}

@Override
public RLock lock(String lockKey, int leaseTime) {
RLock lock
= redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return lock;
}

@Override
public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock
= redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}

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

@Override
public void unlock(String lockKey) {
RLock lock
= redissonClient.getLock(lockKey);
lock.unlock();
}

@Override
public void unlock(RLock lock) {
lock.unlock();
}

public void setRedissonClient(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
}

 

4、redisson属性装配类

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix
= "redisson")
public class RedissonProperties {

private int timeout = 3000;

private String address;

private String password;

private int connectionPoolSize = 64;

private int connectionMinimumIdleSize=10;

private int slaveConnectionPoolSize = 250;

private int masterConnectionPoolSize = 250;

private String[] sentinelAddresses;

private String masterName;

public int getTimeout() {
return timeout;
}

public void setTimeout(int timeout) {
this.timeout = timeout;
}

public int getSlaveConnectionPoolSize() {
return slaveConnectionPoolSize;
}

public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
this.slaveConnectionPoolSize = slaveConnectionPoolSize;
}

public int getMasterConnectionPoolSize() {
return masterConnectionPoolSize;
}

public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
this.masterConnectionPoolSize = masterConnectionPoolSize;
}

public String[] getSentinelAddresses() {
return sentinelAddresses;
}

public void setSentinelAddresses(String sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses.split(",");
}

public String getMasterName() {
return masterName;
}

public void setMasterName(String masterName) {
this.masterName = masterName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public int getConnectionPoolSize() {
return connectionPoolSize;
}

public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}

public int getConnectionMinimumIdleSize() {
return connectionMinimumIdleSize;
}

public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) {
this.connectionMinimumIdleSize = connectionMinimumIdleSize;
}
}

5、SpringBoot自动装配类

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.longge.lock.DistributedLocker;
import com.longge.lock.RedissonDistributedLocker;
import com.longge.lock.RedissonProperties;
import com.longge.utils.RedissLockUtil;

@Configuration
@ConditionalOnClass(Config.
class)
@EnableConfigurationProperties(RedissonProperties.
class)
public class RedissonAutoConfiguration {

@Autowired
private RedissonProperties redssionProperties;

/**
* 哨兵模式自动装配
*
@return
*/
@Bean
@ConditionalOnProperty(name
="redisson.master-name")
RedissonClient redissonSentinel() {
Config config
= new Config();
SentinelServersConfig serverConfig
= config.useSentinelServers().addSentinelAddress(redssionProperties.getSentinelAddresses())
.setMasterName(redssionProperties.getMasterName())
.setTimeout(redssionProperties.getTimeout())
.setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize())
.setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize());

if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword());
}
return Redisson.create(config);
}

/**
* 单机模式自动装配
*
@return
*/
@Bean
@ConditionalOnProperty(name
="redisson.address")
RedissonClient redissonSingle() {
Config config
= new Config();
SingleServerConfig serverConfig
= config.useSingleServer()
.setAddress(redssionProperties.getAddress())
.setTimeout(redssionProperties.getTimeout())
.setConnectionPoolSize(redssionProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize());

if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword());
}

return Redisson.create(config);
}

/**
* 装配locker类,并将实例注入到RedissLockUtil中
*
@return
*/
@Bean
DistributedLocker distributedLocker(RedissonClient redissonClient) {
DistributedLocker locker
= new RedissonDistributedLocker();
locker.setRedissonClient(redissonClient);
RedissLockUtil.setLocker(locker);
return locker;
}

}

6、Lock帮助类

import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;

import DistributedLocker;

/**
* redis分布式锁帮助类
*
@author yangzhilong
*
*/
public class RedissLockUtil {
private static DistributedLocker redissLock;

public static void setLocker(DistributedLocker locker) {
redissLock
= locker;
}

/**
* 加锁
*
@param lockKey
*
@return
*/
public static RLock lock(String lockKey) {
return redissLock.lock(lockKey);
}

/**
* 释放锁
*
@param lockKey
*/
public static void unlock(String lockKey) {
redissLock.unlock(lockKey);
}

/**
* 释放锁
*
@param lock
*/
public static void unlock(RLock lock) {
redissLock.unlock(lock);
}

/**
* 带超时的锁
*
@param lockKey
*
@param timeout 超时时间 单位:秒
*/
public static RLock lock(String lockKey, int timeout) {
return redissLock.lock(lockKey, timeout);
}

/**
* 带超时的锁
*
@param lockKey
*
@param unit 时间单位
*
@param timeout 超时时间
*/
public static RLock lock(String lockKey, TimeUnit unit ,int timeout) {
return redissLock.lock(lockKey, unit, timeout);
}

/**
* 尝试获取锁
*
@param lockKey
*
@param waitTime 最多等待时间
*
@param leaseTime 上锁后自动释放锁时间
*
@return
*/
public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
return redissLock.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);
}

/**
* 尝试获取锁
*
@param lockKey
*
@param unit 时间单位
*
@param waitTime 最多等待时间
*
@param leaseTime 上锁后自动释放锁时间
*
@return
*/
public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
return redissLock.tryLock(lockKey, unit, waitTime, leaseTime);
}
}

 

属性文件实例:

1、单机模式

# redisson lock
redisson.address
=redis://10.18.75.115:6379
redisson.password=

这里如果不加redis://前缀会报URI构建错误,

Caused by: java.net.URISyntaxException: Illegal character in scheme name at index 0

其次,在redis进行连接的时候如果不对密码进行空判断,会出现AUTH校验失败的情况

Caused by: org.redisson.client.RedisException: ERR Client sent AUTH, but no password is set. channel

 

2、哨兵模式

redisson.master-name=mymaster
redisson.password
=xxxx
redisson.sentinel
-addresses=10.47.91.83:26379,10.47.91.83:26380,10.47.91.83:26381

更多的配置信息可以去官网查看