随着微服务的推广,现在越来越多的有一定体量的项目,都开始微服务化。
微服务的优点,不必赘述。但是,凡是都是有两面的,比如,分布式的并发安全性。在单应用中,我们可以通过加锁的方式去实现。那么在分布式项目中,我们同样是遵循加锁的思路。
redisson作为一个redis客户端,自身就有分布式锁的功能。
1.在spring boot项目中配置redisson
1.1添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.11.4</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.4</version>
</dependency>
1.2配置RedissonClient
通过 redisson-spring-boot-starter 源码可见,这里使用的RedisTemplate 就是srping-data-redis的RedisTemplate。所以,我们需要先配置spring redis.
spring:
redis:
host: 127.0.0.1
port: 6379
这样,一个单机服务就可以跑起来了。不过,redisson还有其他配置,这些配置是独立于redis的,需要单独去配置。
在org.redisson.spring.starter.RedissonProperties 这个类中,可以看到
@ConfigurationProperties(prefix = "spring.redis.redisson")
public class RedissonProperties {
private String config;
public String getConfig() {
return config;
}
public void setConfig(String config) {
this.config = config;
}
}
可见,这里的config 一个配置文件地址,所以,我们可以这么配置
application.yaml
spring:
redis:
host: 127.0.0.1
port: 6379
redisson:
config: "classpath:redisson.yaml"
redisson.yaml
singleServerConfig: address: "127.0.0.1:6379" password: null clientName: null database: 7 #选择使用哪个数据库0~15 idleConnectionTimeout: 10000 pingTimeout: 1000 connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500 reconnectionTimeout: 3000 failedAttempts: 3 subscriptionsPerConnection: 5 subscriptionConnectionMinimumIdleSize: 1 subscriptionConnectionPoolSize: 50 connectionMinimumIdleSize: 32 connectionPoolSize: 64 dnsMonitoringInterval: 5000 #dnsMonitoring: false threads: 0 nettyThreads: 0 codec: class: "org.redisson.codec.JsonJacksonCodec" transportMode: "NIO"
注意,这里的配置文件里key 要用驼峰写法,不能用中横线
2.使用redisson
2.1redisson支持的锁,基本有如下几种:
- 可重入锁(Reentrant Lock)
- 公平锁(Fair Lock)
- 联锁(MultiLock)
- 红锁(RedLock)
- 读写锁(ReadWriteLock)
可重入锁:是最基本的锁,一旦上锁,其他的应用在未获得锁的时候,不能读也不能写
@Test
public void lock(){
RLock rLock = redissonClient.getLock("lock");
try {
// rLock.lock(30, TimeUnit.SECONDS);
rLock.tryLock(3,100, TimeUnit.SECONDS);
System.out.println("get lock1");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rLock.unlock();
}
}
lock 中的两个参数 第一个是锁的保持时间,超过这个时间,锁会自动释放,第二个是单位。如果第一个参数是-1 ,则必须等到执行锁释放,其他的操作才能拿到这个锁。
tryLock 第一参数是尝试时间,超过则放弃,后面两个参数与lock一样。
公平锁: 保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程
@Test
public void fairLock(){
RLock fairLock = redissonClient.getFairLock("lock");
try{
fairLock.lock(10, TimeUnit.SECONDS);
System.out.println("get fair lock");
}finally {
fairLock.unlock();
}
}
联锁:可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例
我没有多个redisson实例,就网上找了个
public void testMultiLock(RedissonClient redisson1,RedissonClient redisson2, RedissonClient redisson3){
RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
try {
// 同时加锁:lock1 lock2 lock3, 所有的锁都上锁成功才算成功。
lock.lock();
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
红锁:实现了Redlock介绍的加锁算法
这个也是网上抄的
public void testRedLock(RedissonClient redisson1,RedissonClient redisson2, RedissonClient redisson3){
RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
try {
// 同时加锁:lock1 lock2 lock3, 红锁在大部分节点上加锁成功就算成功。
lock.lock();
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
读写锁:读锁和写锁,互不干扰
@Test
public void lockWriteLock(){
RReadWriteLock rLock = redissonClient.getReadWriteLock("lock");
try {
rLock.writeLock().lock(30, TimeUnit.SECONDS);
// rLock.writeLock().lock();
System.out.println("get write lock");
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rLock.writeLock().unlock();
}
}
@Test
public void lockWriteLock2(){
RReadWriteLock rLock = redissonClient.getReadWriteLock("lock");
rLock.writeLock().lock(30, TimeUnit.SECONDS);
System.out.println("get write lock2");
rLock.writeLock().unlock();
}
@Test
public void lockRead(){
RReadWriteLock rLock = redissonClient.getReadWriteLock("lock");
rLock.readLock().lock(30, TimeUnit.SECONDS);
System.out.println("get read lock");
rLock.readLock().unlock();
}
参考文档: