实际开发中会遇到分布式锁的情况,解决方案有数据库(不推荐)、Redis(Redission 推荐)、zookeeper等
本文主要是springBoot2.x整合Redission的demo,所以并没有整合数据库和mybatis等组件
https://github.com/qidasheng2012/springboot2.x_redis/tree/branch-redisson
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.0.RELEASEversion>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>springboot2.x_redisartifactId>
<version>1.0.0version>
<name>springboot2.x_redisname>
<description>SpringBoot2.x demo project for Redisdescription>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
<dependency>
<groupId>org.redissongroupId>
<artifactId>redissonartifactId>
<version>3.11.0version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.8.1version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.4version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
相比properties文件结构更清晰【推荐使用yml文件】
server:
port: 80
spring:
redis:
#数据库索引
database: 0
host: 127.0.0.1
port: 6379
password: root
lettuce:
pool:
#最大连接数
max-active: 8
#最大阻塞等待时间(负数表示没限制)
max-wait: -1
#最大空闲
max-idle: 8
#最小空闲
min-idle: 0
#连接超时时间
timeout: 10000
package com.example.springboot_redis.redis;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Redis配置文件的参数
*
* @author qp
* @date 2019/7/19 17:35
*/
@Data
@Component
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private String host;
private int port;
private String password;
private int database;
}
package com.example.springboot_redis.redis;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Redission配置类
*
* @author qp
* @date 2019/7/19 14:49
*/
@Slf4j
@Configuration
public class RedissionConfig {
@Autowired
private RedisProperties redisProperties;
@Bean
public RedissonClient redissonClient() {
RedissonClient redissonClient;
Config config = new Config();
String url = "redis://" + redisProperties.getHost() + ":" + redisProperties.getPort();
config.useSingleServer().setAddress(url)
.setPassword(redisProperties.getPassword())
.setDatabase(redisProperties.getDatabase());
try {
redissonClient = Redisson.create(config);
return redissonClient;
} catch (Exception e) {
log.error("RedissonClient init redis url:[{}], Exception:", url, e);
return null;
}
}
}
package com.example.springboot_redis.redis;
import lombok.extern.slf4j.Slf4j;
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;
/**
* 分布式Redis锁
*
* @author qp
* @date 2019/7/19 15:21
*/
@Slf4j
@Component
public class DistributedRedisLock {
@Autowired
private RedissonClient redissonClient;
// 加锁
public Boolean lock(String lockName) {
try {
if (redissonClient == null) {
log.info("DistributedRedisLock redissonClient is null");
return false;
}
RLock lock = redissonClient.getLock(lockName);
// 锁10秒后自动释放,防止死锁
lock.lock(10, TimeUnit.SECONDS);
log.info("Thread [{}] DistributedRedisLock lock [{}] success", Thread.currentThread().getName(), lockName);
// 加锁成功
return true;
} catch (Exception e) {
log.error("DistributedRedisLock lock [{}] Exception:", lockName, e);
return false;
}
}
// 释放锁
public Boolean unlock(String lockName) {
try {
if (redissonClient == null) {
log.info("DistributedRedisLock redissonClient is null");
return false;
}
RLock lock = redissonClient.getLock(lockName);
lock.unlock();
log.info("Thread [{}] DistributedRedisLock unlock [{}] success", Thread.currentThread().getName(), lockName);
// 释放锁成功
return true;
} catch (Exception e) {
log.error("DistributedRedisLock unlock [{}] Exception:", lockName, e);
return false;
}
}
}
package com.example.springboot_redis.contoller;
import com.example.springboot_redis.redis.DistributedRedisLock;
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;
/**
* 分布式Redis锁测试controller
*
* @author qp
* @date 2019/7/19 17:30
*/
@RestController
@RequestMapping("/lock")
public class LockTestController {
@Autowired
private DistributedRedisLock distributedRedisLock;
// 测试分布式锁
@GetMapping("/testLock")
public void testLock() {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Boolean lockFlag = distributedRedisLock.lock("LOCK");
}
}).start();
}
}
}
1、调用方法启用5个线程,每个线程都去获取分布式锁,只有一个线程能获取到锁,其他线程均处于阻塞状态
2、因为没有调用释放锁的方法,且在获取锁的lock()方法中设置了锁的最大时间为10秒(防止死锁的发生),所以在10秒后锁自动释放,由其他线程进行竞争这把分布式锁然后执行
在分布式的情况下可以使用redission作为分布式锁,当然还有ZK、数据库的乐观锁(version)或悲观锁(for update)等等根据具体的业务场景进行选择
在实际使用redission作为分布式锁时,操作步骤如下:
SpringBoot + Redis分布式锁
SpringBoot + Redis分布式锁实战