随着微服务的推广,现在越来越多的有一定体量的项目,都开始微服务化。
微服务的优点,不必赘述。但是,凡是都是有两面的,比如,分布式的并发安全性。在单应用中,我们可以通过加锁的方式去实现。那么在分布式项目中,我们同样是遵循加锁的思路。
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(); }
参考文档: