分布式锁一般有三种实现方式:
1.基于数据库的乐观锁
2.基于redis的分布式锁
3.基于zookeepr的分布式锁
本文介绍第二种,基于redis的分布式锁,官方推荐用redisson,redisson支持四种连接方式,文中写了两种一个是单体,一个是集群
Cluster集群,Sentinel哨兵,Master/Slave主从,Single单体
代码中有详细注释,就不介绍具体方法了
首先写一个属性配置类,用来注入redis锁的一些配置信息:
package com.xhx.springboot.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* xuhaixing
* 2018/7/2 18:16
**/
@ConfigurationProperties(prefix ="redisson")
public class RedissonProperties {
private String address;
private String password;
//毫秒
private int timeout=30000;
private int database=0;
private String sentinelAddress;
private int connectionPoolSize=150;
private int connectionMiniumIdleSize=10;
private int slaveConnectionPoolSize=300;
private int masterConnectionPoolSize=300;
private String[] sentinelAddresses;
private String[] masterAddresses;
//毫秒
private int scanInterval=2000;
private String masterName;
public String[] getMasterAddresses() {
return masterAddresses;
}
public void setMasterAddresses(String[] masterAddresses) {
this.masterAddresses = masterAddresses;
}
public int getScanInterval() {
return scanInterval;
}
public void setScanInterval(int scanInterval) {
this.scanInterval = scanInterval;
}
public int getMasterConnectionPoolSize() {
return masterConnectionPoolSize;
}
public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
this.masterConnectionPoolSize = masterConnectionPoolSize;
}
public String getMasterName() {
return masterName;
}
public void setMasterName(String masterName) {
this.masterName = masterName;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public int getConnectionMiniumIdleSize() {
return connectionMiniumIdleSize;
}
public void setConnectionMiniumIdleSize(int connectionMiniumIdleSize) {
this.connectionMiniumIdleSize = connectionMiniumIdleSize;
}
public int getSlaveConnectionPoolSize() {
return slaveConnectionPoolSize;
}
public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
this.slaveConnectionPoolSize = slaveConnectionPoolSize;
}
public String[] getSentinelAddresses() {
return sentinelAddresses;
}
public void setSentinelAddresses(String[] sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getDatabase() {
return database;
}
public void setDatabase(int database) {
this.database = database;
}
public String getSentinelAddress() {
return sentinelAddress;
}
public void setSentinelAddress(String sentinelAddress) {
this.sentinelAddress = sentinelAddress;
}
}
写一个redisson.properties配置文件
redisson.address=redis://192.168.94.151:6379
redisson.password=xuhaixing
redisson.timeout=3000
redisson.database=0
maven中引入redisson的包
4.0.0
com.xhx.springboot
distribute-lock
0.0.1-SNAPSHOT
jar
distribute-lock
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
UTF-8
UTF-8
1.8
org.redisson
redisson
3.6.5
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.apache.commons
commons-lang3
3.7
org.springframework.boot
spring-boot-maven-plugin
配置类,注入RedissonClient,程序中用RedissonClient操作锁
package com.xhx.springboot.config;
import com.xhx.springboot.properties.RedissonProperties;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xuhaixing
* 2018/7/2 18:25
**/
@Configuration
@AutoConfigureAfter(value = {RedissonProperties.class})
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redissonProperties;
/**
* 单体的
* @return
*/
@Bean
@ConditionalOnProperty(value = "redisson.address")
public RedissonClient redissonSingle(){
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer().setAddress(redissonProperties.getAddress())
.setTimeout(redissonProperties.getTimeout())
.setDatabase(redissonProperties.getDatabase())
.setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redissonProperties.getConnectionMiniumIdleSize());
if(StringUtils.isNotEmpty(redissonProperties.getPassword())){
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 集群的
* @return
*/
@Bean
@ConditionalOnProperty(value = "redisson.masterAddresses")
public RedissonClient redissonSentinel(){
Config config = new Config();
ClusterServersConfig serverConfig = config.useClusterServers().addNodeAddress(redissonProperties.getMasterAddresses())
.setTimeout(redissonProperties.getTimeout())
//设置集群扫描时间
.setScanInterval(redissonProperties.getScanInterval())
//主节点线程池数量
.setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
//从节点线程池数量
.setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());
if(StringUtils.isNotEmpty(redissonProperties.getPassword())){
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
}
封装了一个分布式锁的父类:
package com.xhx.springboot.lock;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* xuhaixing
* 2018/7/2 17:56
**/
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);
}
子类:
package com.xhx.springboot.lock;
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;
/**
* xuhaixing
* 2018/7/2 18:03
**/
@Component
public class RedissonDistributedLocker implements DistributedLocker {
@Autowired
private RedissonClient redissonClient;
/**
* 没有超时时间,默认30s
* @param lockKey
* @return
*/
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
/**
* 自己设置超时时间
* @param lockKey 锁的key
* @param timeout 秒 如果是-1,直到自己解锁,否则不会自动解锁
* @return
*/
@Override
public RLock lock(String lockKey, int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, 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;
}
/**
*
* @param lockKey 锁key
* @param unit 锁单位
* @param waitTime 等到最大时间,强制获取锁
* @param leaseTime 锁失效时间
* @return
*/
@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) {
e.printStackTrace();
}
return false;
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void unlock(RLock lock) {
lock.unlock();
}
}
启动类:
package com.xhx.springboot;
import com.xhx.springboot.properties.RedissonProperties;
import org.redisson.api.RedissonClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication
@PropertySource(value = {"classpath:redisson.properties"})
@EnableConfigurationProperties(value = {RedissonProperties.class})
public class DistributeLockApplication {
public static void main(String[] args) {
SpringApplication.run(DistributeLockApplication.class, args);
}
}
测试类:
package com.xhx.springboot;
import com.xhx.springboot.lock.RedissonDistributedLocker;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class DistributeLockApplicationTests {
@Autowired
private RedissonDistributedLocker redissonLocker;
@Test
public void contextLoads() throws Exception {
int count = 10;
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
Thread thread = new Thread(() -> {
try {
String lockKey = "17631701110";
redissonLocker.tryLock(lockKey, TimeUnit.SECONDS, 100, 8);
System.out.println("===加锁===" + Thread.currentThread().getName());
System.out.println("===做自己操作===");
Thread.sleep(5000);
System.out.println("===释放锁===" + Thread.currentThread().getName());
redissonLocker.unlock(lockKey);
System.out.println(latch.getCount());
} catch (Exception e) {
e.printStackTrace();
}
latch.countDown();
});
thread.start();
}
latch.await();
}
}
程序执行结果:
我的github代码地址