3. [series] Redis Redis of advanced applications - Distributed Lock

Original: 3. [series] Redis Redis of advanced applications - Distributed Lock

During the development of distributed application logic, often encounter concurrency issues.
For example, we modify a user's information, you first need to obtain user information, and then modified in memory, then save it back. If this process there are other threads simultaneously, it will produce the concurrency issues, because reading and storage are not atomic. We need to limit the concurrent execution of the program through a distributed lock.

1. Distributed Lock

Distributed Lock on Redis which is essentially accounted for in a parking space, when new vehicles coming, we found already had a car parked in the parking space, only to give up or come back later.

We use the command to simulate follows:

> setnx lock:qqsir true
OK
...do something ... 
>del lock:qqsir 

But there is such a problem, if in the middle of logic problems, resulting in no execution del, which would fall into a deadlock, the lock will never be released.

Improved 1: We can set an expiration time for the key, so even if the logic behind the error, after the expiration of the time can also lock is freed

> setnx lock:qqsir true
OK
>expire lock:qqsir 5
...do something ... 
>del lock:qqsir 

More logical but it is still a problem, assuming that a sudden power between setnx and expire, did not execute, so still can not release the lock. The root cause of this problem is setnx and expire actions are not atomic.

To solve this problem, there have been a bunch of open source community distributed lock the library. Specifically address this issue and realize the logic is very complicated.

In order to control this chaos, Redis2.8 version joined the extended parameter set, yes setnx and expire instructions can be executed together, solve the distributed lock chaos.

> setnx lock:qqsir true ex 5 nx
OK
>expire lock:qqsir 5
...do something ... 
>del lock:qqsir 

These are the atomic instruction setnx and expire.

Timeout problem

Above added a expiration time is 5s, if logic execution in the middle of greater than 5s, the logic behind the lack of implementation, in order to avoid this problem, Redis distributed lock logic is not too long, if you really need the occasional manual involved.

Reentrancy

Reentrancy is the case holding the lock, the lock request again, if a lock support multiple threads of the same lock that the thread is re-entrant nature. For example ReentrantLock java language is a kind of reentrant lock. Redis reentrant lock, the encapsulated set of methods, the ThreadLocal variables store the current count of the thread holds the lock only.

The following is a java version of the reentrant lock to achieve

public class RedisWithReentrantLock {

 private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>();

 private Jedis jedis;

 public RedisWithReentrantLock(Jedis jedis) {
   this.jedis = jedis;
 }

 private boolean _lock(String key) {
   return jedis.set(key, "", "nx", "ex", 5L) != null;
 }

 private void _unlock(String key) {
   jedis.del(key);
 }

 private Map<String, Integer> currentLockers() {
   Map<String, Integer> refs = lockers.get();
   if (refs != null) {
     return refs;
   }
   lockers.set(new HashMap<>());
   return lockers.get();
 }

 public boolean lock(String key) {
   Map<String, Integer> refs = currentLockers();
   Integer refCnt = refs.get(key);
   if (refCnt != null) {
     refs.put(key, refCnt + 1);
     return true;
   }
   boolean ok = this._lock(key);
   if (!ok) {
     return false;
   }
   refs.put(key, 1);
   return true;
 }

 public boolean unlock(String key) {
   Map<String, Integer> refs = currentLockers();
   Integer refCnt = refs.get(key);
   if (refCnt == null) {
     return false;
   }
   refCnt -= 1;
   if (refCnt > 0) {
     refs.put(key, refCnt);
   } else {
     refs.remove(key);
     this._unlock(key);
   }
   return true;
 }

 public static void main(String[] args) {
   Jedis jedis = new Jedis();
   RedisWithReentrantLock redis = new RedisWithReentrantLock(jedis);
   System.out.println(redis.lock("codehole"));
   System.out.println(redis.lock("codehole"));
   System.out.println(redis.unlock("codehole"));
   System.out.println(redis.unlock("codehole"));
 }

}

Guess you like

Origin www.cnblogs.com/lonelyxmas/p/12515045.html