How to implement distributed lock with Redlock

forward from:

http://blog.csdn.net/forezp/article/details/70305336 
This article is from Fang Zhipeng's blog

 

I wrote an article before "How to implement distributed locks in the springcloud distributed system? , because I just read the relevant books and consulted the relevant materials, I think that is feasible. The general idea of ​​that article is to use the setNx command in conjunction with setEx. setNx is a time-consuming operation because it needs to query whether the key exists. Even if Redis has millions of qps, this operation is problematic in high concurrency scenarios. Regarding the implementation of distributed locks in redis , redis officially recommends the use of redlock.

1. Introduction to redlock

Distributed locks are a very useful technical means when different processes need to access shared resources mutually exclusive. There are three properties to consider to implement efficient distributed locks:

  • Security properties: mutual exclusion, no matter when, only one client holds the lock
  • Efficiency attribute A: no deadlock
  • Efficiency attribute B: fault tolerance, as long as most redis nodes can work normally, the client can acquire and release locks.

Redlock is an algorithm officially proposed by redis to implement a distributed lock manager . This algorithm will be more secure and reliable than the general ordinary method. A discussion of this algorithm can be found in the official documentation .

2. How to use redlock with java

Introduce redis and redisson dependencies in the pom file:

 

<!-- redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.3.2</version>
        </dependency>

 The AquiredLockWorker interface class is mainly used for the logic that needs to be processed after acquiring the lock:

 

 

/**
 * Created by fangzhipeng on 2017/4/5.
 * The logic that needs to be processed after acquiring the lock
 */
public interface AquiredLockWorker<T> {
     T invokeAfterLockAquire() throws Exception;
}

 DistributedLocker gets the lock management class:

 

 

/**
 * Created by fangzhipeng on 2017/4/5.
 * Get the lock management class
 */
public interface DistributedLocker {

     /**
      * Get the lock
      * @param resourceName the name of the lock
      * @param worker processing class after acquiring the lock
      * @param <T>
      * @return The data to be returned after processing the specific business logic
      * @throws UnableToAquireLockException
      * @throws Exception
      */
     <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws UnableToAquireLockException, Exception;

     <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception;

}

 UnableToAquireLockException , the exception class that cannot acquire the lock:

 

 

/**
 * Created by fangzhipeng on 2017/4/5.
 * exception class
 */
public class UnableToAquireLockException extends RuntimeException {

    public UnableToAquireLockException() {
    }

    public UnableToAquireLockException(String message) {
        super(message);
    }

    public UnableToAquireLockException(String message, Throwable cause) {
        super(message, cause);
    }
}

 RedissonConnector connection class:

 

 

/**
 * Created by fangzhipeng on 2017/4/5.
 * Get the RedissonClient connection class
 */
@Component
public class RedissonConnector {
    RedissonClient redisson;
    @PostConstruct
    public void init(){
        redisson = Redisson.create();
    }

    public RedissonClient getClient(){
        return redisson;
    }

}

 The RedisLocker class, which implements DistributedLocker:

 

 

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;

/**
 * Created by fangzhipeng on 2017/4/5.
 */
@Component
public class RedisLocker  implements DistributedLocker{

    private final static String LOCKER_PREFIX = "lock:";

    @Autowired
    RedissonConnector redissonConnector;
    @Override
    public <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws InterruptedException, UnableToAquireLockException, Exception {

        return lock(resourceName, worker, 100);
    }

    @Override
    public <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception {
        RedissonClient redisson= redissonConnector.getClient();
        RLock lock = redisson.getLock(LOCKER_PREFIX + resourceName);
      // Wait for 100 seconds seconds and automatically unlock it after lockTime seconds
        boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);
        if (success) {
            try {
                return worker.invokeAfterLockAquire();
            } finally {
                lock.unlock();
            }
        }
        throw new UnableToAquireLockException();
    }
}

 Test class:

 

 

@Autowired
    RedisLocker distributedLocker;
    @RequestMapping(value = "/redlock")
    public String testRedlock() throws Exception{

        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(5);
        for (int i = 0; i < 5; ++i) { // create and start threads
            new Thread(new Worker(startSignal, doneSignal)).start();
        }
        startSignal.countDown(); // let all threads proceed
        doneSignal.await();
        System.out.println("All processors done. Shutdown connection");
        return "redlock";
    }

     class Worker implements Runnable {
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;

        Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
        }

        public void run() {
            try {
                startSignal.await();
                distributedLocker.lock("test",new AquiredLockWorker<Object>() {

                    @Override
                    public Object invokeAfterLockAquire() {
                        doTask();
                        return null;
                    }

                });
            }catch (Exception e){

            }
        }

        void doTask() {
            System.out.println(Thread.currentThread().getName() + " start");
            Random random = new Random();
            int _int = random.nextInt(200);
            System.out.println(Thread.currentThread().getName() + " sleep " + _int + "millis");
            try {
                Thread.sleep(_int);
            } catch (InterruptedException e) {
                e.printStackTrace ();
            }
            System.out.println(Thread.currentThread().getName() + " end");
            doneSignal.countDown();
        }
    }

 Run the test class:

Thread-48 start
Thread-48 sleep 99millis
Thread-48 end
Thread-49 start
Thread-49 sleep 118millis
Thread-49 end
Thread-52 start
Thread-52 sleep 141millis
Thread-52 end
Thread-50 start
Thread-50 sleep 28millis
Thread-50 end
Thread-51 start
Thread-51 sleep 145millis
Thread-51 end

 

From the running results, in the case of asynchronous tasks, it is true that the thread cannot be run until the lock is acquired. In any case, this is a solution officially recommended by redis, and the reliability is relatively high. If you have any questions, please leave a message.

3. References

https://github.com/redisson/redisson

"Redis Official Documentation" Building distributed locks with Redis

A Look at the Java Distributed In-Memory Data Model (Powered by Redis)

Excellent article recommendation:

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326967933&siteId=291194637