springboot集成redisson实现分布式锁
springboot中集成redisson-spring-boot-starter,
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>boot-redisson</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot-redisson</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.boot.redisson.BootRedissonApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
单机配置,和集权配置
单机:
application.yml
spring:
redis:
port: 6379
host: 127.0.0.1
password: "123456"
lettuce:
pool:
# 连接池中的最大空闲连接 默认8
max-idle: 8
# 连接池中的最小空闲连接 默认0
min-idle: 0
# 连接池最大连接数 默认8 ,负数表示没有限制
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1
max-wait: -1
集群配置:application.yml
spring:
redis:
password: '123456' #Redis服务器的登录密码
cluster:
enable: true
max-redirects: 5
nodes:
- 10.100.232.181:6379
- 10.100.232.181:6380
- 10.100.232.182:6379
- 10.100.232.182:6380
- 10.100.232.183:6379
- 10.100.232.183:6380
和普通集成redis的配置一样。以前集成redis的,不需要修改
分布式锁接口定义
package com.boot.redisson.lock;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* 描述: 锁抽象接口 <br>
* 时间: 2021-06-22 9:29 <br>
* 作者:IT学习道场
*/
public interface IRedissonLocker {
/**
* 加锁
* @param lockKey
* @return
*/
RLock lock(String lockKey);
/**
* 加锁,并且设置过期时间
* @param lockKey 锁 key
* @param timeout 过期时间 单位默认 = 秒
* @return
*/
RLock lock(String lockKey, int timeout);
/**
* 加锁
* @param lockKey 锁 key
* @param unit 过期时间
* @param timeout 时间单位
* @return
*/
RLock lock(String lockKey, TimeUnit unit, int timeout);
/**
* 常识加锁
* @param lockKey 锁 key
* @param unit 时间单位
* @param waitTime 最长等待取锁时间,如果再这个时间内取到锁将返回true,如果超过这个时间还没取到锁将返回false
* @param leaseTime 取到锁之后锁过期时间,当超过这个时间还没执行完业务锁将被释放。
* @return
*/
boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);
/**
* 释放锁
* @param lockKey
*/
void unlock(String lockKey);
/**
* 释放锁
* @param lock
*/
void unlock(RLock lock);
/**
* 强制释放锁
* @param lockKey
*/
void forceUnlock(String lockKey);
/**
* 强制释放锁
* @param lock
*/
void forceUnlock(RLock lock);
/**
* 公平加锁
* @param lockKey
* @return
*/
RLock fairLock(String lockKey);
/**
* 公平加锁,并且设置过期时间
* @param lockKey 锁 key
* @param timeout 过期时间 单位默认 = 秒
* @return
*/
RLock fairLock (String lockKey, int timeout);
/**
* 公平加锁
* @param lockKey 锁 key
* @param unit 过期时间
* @param timeout 时间单位
* @return
*/
RLock fairLock (String lockKey, TimeUnit unit, int timeout);
/**
* 红锁加锁
* @param lockKey
* @return
*/
RLock redLock(String lockKey);
/**
* 红锁加锁,并且设置过期时间
* @param lockKey 锁 key
* @param timeout 过期时间 单位默认 = 秒
* @return
*/
RLock redLock (String lockKey, int timeout);
/**
* 红锁加锁
* @param lockKey 锁 key
* @param unit 过期时间
* @param timeout 时间单位
* @return
*/
RLock redLock (String lockKey, TimeUnit unit, int timeout);
}
redisson的锁实现
package com.boot.redisson.lock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* 描述: Redisson分布式锁实现 <br>
* 时间: 2021-06-22 9:30 <br>
* 作者:IT学习道场
*/
@Component
public class RedissonLocker implements IRedissonLocker {
@Autowired
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();
}
@Override
public void forceUnlock(String lockKey) {
RLock lock= redissonClient.getLock(lockKey);
lock.forceUnlock();
}
@Override
public void forceUnlock(RLock lock) {
lock.forceUnlock();
}
@Override
public RLock fairLock(String lockKey) {
RLock fairLock = redissonClient.getFairLock(lockKey);
fairLock.lock();
return fairLock;
}
@Override
public RLock fairLock(String lockKey, int timeout) {
RLock fairLock = redissonClient.getFairLock(lockKey);
fairLock.lock(timeout, TimeUnit.SECONDS);
return fairLock;
}
@Override
public RLock fairLock(String lockKey, TimeUnit unit, int timeout) {
RLock fairLock = redissonClient.getFairLock(lockKey);
fairLock.lock(timeout, unit);
return fairLock;
}
@Override
public RLock redLock(String lockKey) {
RLock lock= redissonClient.getLock(lockKey);
RedissonRedLock redLock = new RedissonRedLock(lock);
redLock.lock();
return redLock;
}
@Override
public RLock redLock(String lockKey, int timeout) {
RLock lock= redissonClient.getLock(lockKey);
RedissonRedLock redLock = new RedissonRedLock(lock);
redLock.lock(timeout, TimeUnit.SECONDS);
return redLock;
}
@Override
public RLock redLock(String lockKey, TimeUnit unit, int timeout) {
RLock lock= redissonClient.getLock(lockKey);
RedissonRedLock redLock = new RedissonRedLock(lock);
redLock.lock(timeout, unit);
return redLock;
}
}
使用实例:
package com.boot.redisson.controller;
import com.boot.redisson.lock.RedissonLocker;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* 描述: RedissonController <br>
* 时间: 2021-06-22 8:43 <br>
* 作者:IT学习道场
*/
@RestController
@RequestMapping("/redisson")
public class RedissonController {
@Autowired
private RedissonLocker locker;
@GetMapping("/lock")
private void lock(){
RLock lock = locker.lock("lock");
try {
if( lock.isLocked() ) {
System.out.println("拿到锁了开始执行业务");
Thread.sleep(30000);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
@GetMapping("/tryLock")
private void tryLock(){
boolean locked = locker.tryLock("tryLock", TimeUnit.SECONDS, 20, 10);
try {
if( locked ) {
System.out.println("拿到锁了开始执行业务");
Thread.sleep(30000);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
locker.unlock("tryLock");
}
}
}
这里另外赠送 RedissonClient和 RedisTemplate同步序列化,都采用jackson,实现RedissonClient和 RedisTemplate相互存取数据
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.redisson.spring.starter.RedissonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ReflectionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 描述: RedisTemplateConfig <br>
* 时间: 2021-06-22 9:16 <br>
* 作者:IT学习道场
*/
@Configuration
public class RedisTemplateConfig {
@Autowired(required = false)
private List<RedissonAutoConfigurationCustomizer> redissonAutoConfigurationCustomizers;
@Autowired
private RedissonProperties redissonProperties;
@Autowired
private RedisProperties redisProperties;
@Autowired
private ApplicationContext ctx;
/**
* redisTemplate 相关配置
*
* @param redissonConnectionFactory redissonConnectionFactory
* @return org.springframework.data.redis.core.RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedissonConnectionFactory redissonConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redissonConnectionFactory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = initJackson2();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
//init jackson2
private Jackson2JsonRedisSerializer<Object> initJackson2() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
return new RedissonConnectionFactory(redisson);
}
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redisson() throws IOException {
Config config = null;
Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout");
Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, redisProperties);
int timeout;
if(null == timeoutValue){
timeout = 10000;
}else if (!(timeoutValue instanceof Integer)) {
Method millisMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis");
timeout = ((Long) ReflectionUtils.invokeMethod(millisMethod, timeoutValue)).intValue();
} else {
timeout = (Integer)timeoutValue;
}
if (redissonProperties.getConfig() != null) {
try {
InputStream is = getConfigStream();
config = Config.fromJSON(is);
} catch (IOException e) {
// trying next format
try {
InputStream is = getConfigStream();
config = Config.fromYAML(is);
} catch (IOException e1) {
throw new IllegalArgumentException("Can't parse config", e1);
}
}
} else if (redisProperties.getSentinel() != null) {
Method nodesMethod = ReflectionUtils.findMethod(RedisProperties.Sentinel.class, "getNodes");
Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, redisProperties.getSentinel());
String[] nodes;
if (nodesValue instanceof String) {
nodes = convert(Arrays.asList(((String)nodesValue).split(",")));
} else {
nodes = convert((List<String>)nodesValue);
}
config = new Config();
config.useSentinelServers()
.setMasterName(redisProperties.getSentinel().getMaster())
.addSentinelAddress(nodes)
.setDatabase(redisProperties.getDatabase())
.setConnectTimeout(timeout)
.setPassword(redisProperties.getPassword());
} else if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, redisProperties) != null) {
Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, redisProperties);
Method nodesMethod = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
List<String> nodesObject = (List) ReflectionUtils.invokeMethod(nodesMethod, clusterObject);
String[] nodes = convert(nodesObject);
config = new Config();
config.useClusterServers()
.addNodeAddress(nodes)
.setConnectTimeout(timeout)
.setPassword(redisProperties.getPassword());
} else {
config = new Config();
String prefix = "redis://";
Method method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
if (method != null && (Boolean)ReflectionUtils.invokeMethod(method, redisProperties)) {
prefix = "rediss://";
}
config.useSingleServer()
.setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
.setConnectTimeout(timeout)
.setDatabase(redisProperties.getDatabase())
.setPassword(redisProperties.getPassword());
}
if (redissonAutoConfigurationCustomizers != null) {
for (RedissonAutoConfigurationCustomizer customizer : redissonAutoConfigurationCustomizers) {
customizer.customize(config);
}
}
config.setCodec(codec());
return Redisson.create(config);
}
private String[] convert(List<String> nodesObject) {
List<String> nodes = new ArrayList<String>(nodesObject.size());
for (String node : nodesObject) {
if (!node.startsWith("redis://") && !node.startsWith("rediss://")) {
nodes.add("redis://" + node);
} else {
nodes.add(node);
}
}
return nodes.toArray(new String[nodes.size()]);
}
private InputStream getConfigStream() throws IOException {
Resource resource = ctx.getResource(redissonProperties.getConfig());
InputStream is = resource.getInputStream();
return is;
}
private Codec codec(){
//使用json序列化方式
Codec codec = new JsonJacksonCodec();
return codec;
}
}
自己去试试吧
作者:IT学习道场
欢迎关注微信公众号 : IT学习道场