Redisson realizes distributed lock principle and code actual combat

Redission distributed lock principle

Video address: https://www.bilibili.com/video/BV15K411j7rM?from=search&seid=14544758179278228735

To implement Redis distributed lock, in addition to implementing it based on the native API of redis client, you can also use the open source framework: Redission

Redisson is an enterprise-level open source Redis Client, which also provides support for distributed locks. I also highly recommend it to everyone, why?

Recall what I said above, if you write your own code to set a value through redis, it is set by the following command.

  • SET anyLock unique_value NX PX 30000

The timeout period set here is 30s. If I have not completed the business logic for more than 30s, the key will expire and other threads may acquire the lock.

In this way, the first thread has not finished executing the business logic, and thread safety issues will also occur when the second thread comes in. So we also need to maintain this expiration time, which is too troublesome~

Let's take a look at how redisson is implemented? First feel the coolness of using redission:

Config config = new Config();
config.useClusterServers()    
.addNodeAddress("redis://192.168.31.101:7001")    .addNodeAddress("redis://192.168.31.101:7002")    .addNodeAddress("redis://192.168.31.101:7003")    .addNodeAddress("redis://192.168.31.102:7001")    .addNodeAddress("redis://192.168.31.102:7002")    .addNodeAddress("redis://192.168.31.102:7003");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("anyLock");
lock.lock();
......
lock.unlock();

It's that simple, we only need to complete the distributed lock through the lock and unlock in its api. He helped us consider many details:

  • All redisson instructions are executed through lua scripts, and redis supports atomic execution of lua scripts

  • Redisson sets the default expiration time of a key to 30s. What if a client holds a lock for more than 30s?

    There is a watchdogconcept of watchdog in redisson, which is translated as watchdog. It will help you set the key timeout time to 30s every 10 seconds after you acquire the lock.

    In this case, even if the lock has been held, the key will not expire and other threads will acquire the lock.

  • Redisson's "watchdog" logic ensures that no deadlock occurs.

    (If the machine is down, the watchdog will disappear. At this time, the key expiration time will not be extended. After 30s, it will automatically expire, and other threads can acquire the lock)

img

In addition, redisson also provides support for the redlock algorithm,

Its usage is also very simple:

RedissonClient redisson = Redisson.create(config);
RLock lock1 = redisson.getFairLock("lock1");
RLock lock2 = redisson.getFairLock("lock2");
RLock lock3 = redisson.getFairLock("lock3");
RedissonRedLock multiLock = new RedissonRedLock(lock1, lock2, lock3);
multiLock.lock();
...
multiLock.unlock();

summary:

This section analyzes the specific landing plan of using redis as a distributed lock and some of its limitations, and then introduces a redis client framework redisson, which I recommend everyone to use, and it will be less careful than writing code to achieve a lot of details.

Distributed lock actual combat based on redission

In a distributed environment, many scenarios, such as: spike, ID generation...all need distributed locks. The realization of distributed locks can be based on the temporary nodes of redis setnx and zk. . . Today we introduce a method officially recommended by redis-redission.

1 、 pom.xml

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.2.3</version>
</dependency>

2、RedissionUtils

public class RedissionUtils {
	private static Logger logger = LoggerFactory.getLogger(RedissionUtils.class);
 
	private static RedissionUtils redisUtils;
 
	private RedissionUtils() {
	}
 
	/**
	 * 提供单例模式
	 * 
	 * @return
	 */
	public static RedissionUtils getInstance() {
		if (redisUtils == null)
			synchronized (RedisUtils.class) {
				if (redisUtils == null)
					redisUtils = new RedissionUtils();
			}
		return redisUtils;
	}
 
	/**
	 * 使用config创建Redisson Redisson是用于连接Redis Server的基础类
	 * 
	 * @param config
	 * @return
	 */
	public RedissonClient getRedisson(Config config) {
		RedissonClient redisson = Redisson.create(config);
		logger.info("成功连接Redis Server");
		return redisson;
	}
 
	/**
	 * 使用ip地址和端口创建Redisson
	 * 
	 * @param ip
	 * @param port
	 * @return
	 */
	public RedissonClient getRedisson(String ip, String port) {
		Config config = new Config();
		config.useSingleServer().setAddress(ip + ":" + port);
		RedissonClient redisson = Redisson.create(config);
		logger.info("成功连接Redis Server" + "\t" + "连接" + ip + ":" + port + "服务器");
		return redisson;
	}
 
	/**
	 * 关闭Redisson客户端连接
	 * 
	 * @param redisson
	 */
	public void closeRedisson(RedissonClient redisson) {
		redisson.shutdown();
		logger.info("成功关闭Redis Client连接");
	}
 
	/**
	 * 获取字符串对象
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <T> RBucket<T> getRBucket(RedissonClient redisson, String objectName) {
		RBucket<T> bucket = redisson.getBucket(objectName);
		return bucket;
	}
 
	/**
	 * 获取Map对象
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <K, V> RMap<K, V> getRMap(RedissonClient redisson, String objectName) {
		RMap<K, V> map = redisson.getMap(objectName);
		return map;
	}
 
	/**
	 * 获取有序集合
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <V> RSortedSet<V> getRSortedSet(RedissonClient redisson,
			String objectName) {
		RSortedSet<V> sortedSet = redisson.getSortedSet(objectName);
		return sortedSet;
	}
 
	/**
	 * 获取集合
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <V> RSet<V> getRSet(RedissonClient redisson, String objectName) {
		RSet<V> rSet = redisson.getSet(objectName);
		return rSet;
	}
 
	/**
	 * 获取列表
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <V> RList<V> getRList(RedissonClient redisson, String objectName) {
		RList<V> rList = redisson.getList(objectName);
		return rList;
	}
 
	/**
	 * 获取队列
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <V> RQueue<V> getRQueue(RedissonClient redisson, String objectName) {
		RQueue<V> rQueue = redisson.getQueue(objectName);
		return rQueue;
	}
 
	/**
	 * 获取双端队列
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <V> RDeque<V> getRDeque(RedissonClient redisson, String objectName) {
		RDeque<V> rDeque = redisson.getDeque(objectName);
		return rDeque;
	}
 
	/**
	 * 此方法不可用在Redisson 1.2 中 在1.2.2版本中 可用
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	/**
	 * public <V> RBlockingQueue<V> getRBlockingQueue(RedissonClient
	 * redisson,String objectName){ RBlockingQueue
	 * rb=redisson.getBlockingQueue(objectName); return rb; }
	 */
 
	/**
	 * 获取锁
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public RLock getRLock(RedissonClient redisson, String objectName) {
		RLock rLock = redisson.getLock(objectName);
		return rLock;
	}
 
	/**
	 * 获取原子数
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public RAtomicLong getRAtomicLong(RedissonClient redisson, String objectName) {
		RAtomicLong rAtomicLong = redisson.getAtomicLong(objectName);
		return rAtomicLong;
	}
 
	/**
	 * 获取记数锁
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public RCountDownLatch getRCountDownLatch(RedissonClient redisson,
			String objectName) {
		RCountDownLatch rCountDownLatch = redisson
				.getCountDownLatch(objectName);
		return rCountDownLatch;
	}
 
	/**
	 * 获取消息的Topic
	 * 
	 * @param redisson
	 * @param objectName
	 * @return
	 */
	public <M> RTopic<M> getRTopic(RedissonClient redisson, String objectName) {
		RTopic<M> rTopic = redisson.getTopic(objectName);
		return rTopic;
	}
 
}

redission supports multiple connection modes

//单机
RedissonClient redisson = Redisson.create();
Config config = new Config();
config.useSingleServer().setAddress("myredisserver:6379");
RedissonClient redisson = Redisson.create(config);
 
 
//主从
Config config = new Config();
config.useMasterSlaveServers()
    .setMasterAddress("127.0.0.1:6379")
    .addSlaveAddress("127.0.0.1:6389", "127.0.0.1:6332", "127.0.0.1:6419")
    .addSlaveAddress("127.0.0.1:6399");
RedissonClient redisson = Redisson.create(config);
 
 
//哨兵
Config config = new Config();
config.useSentinelServers()
    .setMasterName("mymaster")
    .addSentinelAddress("127.0.0.1:26389", "127.0.0.1:26379")
    .addSentinelAddress("127.0.0.1:26319");
RedissonClient redisson = Redisson.create(config);
 
 
//集群
Config config = new Config();
config.useClusterServers()
    .setScanInterval(2000) // cluster state scan interval in milliseconds
    .addNodeAddress("127.0.0.1:7000", "127.0.0.1:7001")
    .addNodeAddress("127.0.0.1:7002");
RedissonClient redisson = Redisson.create(config);

3. Various distributed "locks" based on redission:

1) Reentrant lock:

Redisson's distributed reentrant lock RLock implements the java.util.concurrent.locks.Lock interface and supports automatic expiration unlocking. It also provides asynchronous (Async), reflective (Reactive) and RxJava2 standard interfaces.

// 最常见的使用方法
RLock lock = redisson.getLock("anyLock");
lock.lock();
//...
lock.unlock();
 
//另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
 
// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);
 
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

Everyone knows that if the Redisson node responsible for storing the distributed lock goes down and the lock is in the locked state, the lock will be locked. In order to avoid this situation, Redisson provides a watchdog that monitors the lock. Its function is to continuously extend the validity period of the lock before the Redisson instance is closed. By default, the timeout period for the watchdog to check the lock is 30 seconds, and it can also be specified by modifying Config.lockWatchdogTimeout.

Redisson also provides asynchronous execution methods for distributed locks:

RLock lock = redisson.getLock("anyLock");
lock.lockAsync();
lock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = lock.tryLockAsync(100, 10, TimeUnit.SECONDS);

The RLock object fully complies with the Java Lock specification. That is to say, only the process with the lock can be unlocked, and other processes will throw an IllegalMonitorStateException error when unlocking.

2) Fair Lock:

It ensures that when multiple Redisson client threads request locks at the same time, they are assigned to the thread that made the request first. All request threads will be queued in a queue. When a thread goes down, Redisson will wait for 5 seconds before continuing to the next thread. That is to say, if the first 5 threads are in the waiting state, then the next thread will wait At least 25 seconds. Use the same method as above, use the following methods when obtaining:

RLock fairLock = redisson.getFairLock("anyLock");

 

3) Interlock (MultiLock):

Redisson distributed interlock based on Redis RedissonMultiLock object can associate multiple RLock objects into one interlock, and each RLock object instance can come from a different Redisson instance.

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
 
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();
 
//另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
 
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);
 
// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

4) RedLock:

The Redisson RedLock object based on Redis implements the locking algorithm introduced by Redlock. This object can also be used to associate multiple RLock objects into a red lock, and each RLock object instance can come from a different Redisson instance.

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
 
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();
 
//另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
 
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);
 
// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

5) ReadWriteLock:

The Redis-based Redisson distributed reentrant read-write lock RReadWriteLock Java object implements the java.util.concurrent.locks.ReadWriteLock interface. The read lock and write lock both inherit the RLock interface. Distributed reentrant read-write locks allow multiple read locks and one write lock to be locked at the same time.

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();
 
//另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
 
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);
 
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

6) Semaphore:

Based on Redis Redisson's distributed semaphore (Semaphore) Java object RSemaphore uses a similar interface and usage to java.util.concurrent.Semaphore. It also provides asynchronous (Async), reflective (Reactive) and RxJava2 standard interfaces.

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();
//或
semaphore.acquireAsync();
semaphore.acquire(23);
semaphore.tryAcquire();
//或
semaphore.tryAcquireAsync();
semaphore.tryAcquire(23, TimeUnit.SECONDS);
//或
semaphore.tryAcquireAsync(23, TimeUnit.SECONDS);
semaphore.release(10);
semaphore.release();
//或
semaphore.releaseAsync();

7) PermitExpirableSemaphore:

The Redis-based Redisson expirable semaphore (PermitExpirableSemaphore) is based on the RSemaphore object, adding an expiration time for each signal. Each signal can be identified by an independent ID, and can only be released by submitting this ID when released. It provides asynchronous (Async), reflective (Reactive) and RxJava2 standard interfaces.

RPermitExpirableSemaphore semaphore = redisson.getPermitExpirableSemaphore("mySemaphore");
String permitId = semaphore.acquire();
// 获取一个信号,有效期只有2秒钟。
String permitId = semaphore.acquire(2, TimeUnit.SECONDS);
// ...
semaphore.release(permitId);
  •  

8) Door latch:

The Redisson-based Redisson distributed latch (CountDownLatch) Java object RCountDownLatch uses a similar interface and usage as java.util.concurrent.CountDownLatch.

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();
 
// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

9) Distributed AtomicLong:

RAtomicLong atomicLong = redisson.getAtomicLong("myAtomicLong");
atomicLong.set(3);
atomicLong.incrementAndGet();
atomicLong.get();

10) Distributed BitSet:

RBitSet set = redisson.getBitSet("simpleBitset");
set.set(0, true);
set.set(1812, false);
set.clear(0);
set.addAsync("e");
set.xor("anotherBitset");

11) Distributed Object:

RBucket<AnyObject> bucket = redisson.getBucket("anyObject");
bucket.set(new AnyObject(1));
AnyObject obj = bucket.get();
bucket.trySet(new AnyObject(3));
bucket.compareAndSet(new AnyObject(4), new AnyObject(5));
bucket.getAndSet(new AnyObject(6));

12) Distributed Set:

RSet<SomeObject> set = redisson.getSet("anySet");
set.add(new SomeObject());
set.remove(new SomeObject());

13) Distributed List:

RList<SomeObject> list = redisson.getList("anyList");
list.add(new SomeObject());
list.get(0);
list.remove(new SomeObject());

14) Distributed Blocking Queue:

RBlockingQueue<SomeObject> queue = redisson.getBlockingQueue("anyQueue");
queue.offer(new SomeObject());
SomeObject obj = queue.peek();
SomeObject someObj = queue.poll();
SomeObject ob = queue.poll(10, TimeUnit.MINUTES);

15) Distributed Map:

RMap<String, SomeObject> map = redisson.getMap("anyMap");
SomeObject prevObject = map.put("123", new SomeObject());
SomeObject currentObject = map.putIfAbsent("323", new SomeObject());
SomeObject obj = map.remove("123");
map.fastPut("321", new SomeObject());
map.fastRemove("321");
Future<SomeObject> putAsyncFuture = map.putAsync("321");
Future<Void> fastPutAsyncFuture = map.fastPutAsync("321");
map.fastPutAsync("321", new SomeObject());
map.fastRemoveAsync("321");

In addition, Multimap is also supported.

16)Map eviction:

Now Redis does not have the expired function of clearing an entry in the Map, it can only clear all the entries in the Map. Redission provides this function.

RMapCache<String, SomeObject> map = redisson.getMapCache("anyMap");
// ttl = 10 minutes, 
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES);
// ttl = 10 minutes, maxIdleTime = 10 seconds
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES, 10, TimeUnit.SECONDS);
// ttl = 3 seconds
map.putIfAbsent("key2", new SomeObject(), 3, TimeUnit.SECONDS);
// ttl = 40 seconds, maxIdleTime = 10 seconds
map.putIfAbsent("key2", new SomeObject(), 40, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);

Guess you like

Origin blog.csdn.net/mrlin6688/article/details/107767020