About Redisson's Distributed Lock

Recently, I encountered the problem of distributed locking in my work. The normally used reentrantlock can no longer meet the needs of distribution. The current popular distributed lock zookeeper Redis is waiting on the market. Now I have learned the distributed lock of Redis briefly. I will use it first, and then I will understand the principle deeply, let alone say more.

Requirements for distributed locks

  • Mutual exclusion: Distributed locks need to be mutually exclusive between threads on different nodes. This is fundamental.
  • Re-accessibility: The same thread on the same node can acquire the lock again if it acquires it.
  • Lock timeout: Lock timeout is supported as local locks to prevent deadlocks.
  • High Availability: Locking and unlocking need to be efficient, while also ensuring high availability to prevent distributed lock failures, which can increase downgrades.
  • Supports blocking and non-blocking: lock and trylock as well as tryLock(long timeOut) are supported as ReentrantLock.
  • Supports fair and unfair locks (optional): Fair locks mean that locks are acquired in the order in which they are requested, whereas unfair locks are unordered. This is generally less implemented.

This is the most basic

About Redisson Lock

In fact, we all know that ReentrantLock already has good lock performance and implementation. It has good performance and implementation in mutually exclusive, reentrant, lock timeout, blocking support, fair lock support, but it is not suitable for distributed scenarios. redisson is a distributed lock to make up for this deficiency (there are many distributed locks, others, not discussed here)The RLock interface inherits the Lock interface and naturally elegantly implements the above lock requirements.

principle


Required jar package

        <!-- redis rely on commons-pool This dependency must be added -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <!--Redis Distributed Lock-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.0</version>
        </dependency>

[application.yml]
Clear structure than properties file [yml file recommended]

# Implement Redis Distributed Lock
spring:
  redis:
    database: 0
    host: Your hostname
    password: Your password
    port: 6379
    lettuce:
      pool:
        max-active: 100
        max-wait: -1
        max-idle: 8
        min-idle: 0

Note that this does not necessarily need to be configured by the specified name, so it can be customized

[RedissionConfig]

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Xiang
 * @date 2021/9/8 - 22:27
 */
@Slf4j
@Configuration
public class RedissionConfig {

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    @Value("${spring.redis.database}")
    private int database;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        String REDISSON_PREFIX = "redis://";
        String url = REDISSON_PREFIX + host + ":" + port;
        // Single Redis
        config.useSingleServer()
                .setAddress(url)
                .setPassword(password)
                .setDatabase(database);

        // The actual development process should be clustering or sentinel mode, for example, cluster
        //String[] urls = {"127.0.0.1:6379", "127.0.0.2:6379"};
        //config.useClusterServers()
        //        .addNodeAddress(urls);

        try {
            return Redisson.create(config);
        } catch (Exception e) {
            log.error("RedissonClient init redis url:[{}], Exception:", url, e);
            return null;
        }
    }
}

[DistributedRedisLock]

import lombok.extern.slf4j.Slf4j;
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;

/**
 * @author Xiang
 * @date 2021/9/8 - 22:36
 */
@Slf4j
@Component
public class DistributedRedisLock {

    @Autowired
    RedissonClient redissonClient;

    // Locking
    public Boolean lock(String lockName) {
        if (null == redissonClient) {
            log.info("DistributedRedisLock redissonClient is null");
            return false;
        }

        try {
            RLock lock = redissonClient.getLock(lockName);
            // Lock automatically released for 10 seconds
            lock.lock(10, TimeUnit.SECONDS);
            log.info("Thread [{}] DistributedRedisLock lock [{}] success Lock succeeded", Thread.currentThread().getName(), lockName);
            // Lock Success
            return true;
        } catch (Exception e) {
            log.error("DistributedRedisLock lock [{}] Exception:", lockName, e);
            return false;
        }
    }

    // Release lock
    public Boolean unlock(String lockName) {
        if (redissonClient == null) {
            log.info("DistributedRedisLock redissonClient is null");
            return false;
        }

        try {
            RLock lock = redissonClient.getLock(lockName);
            lock.unlock();
            log.info("Thread [{}] DistributedRedisLock unlock [{}] success Unlock", Thread.currentThread().getName(), lockName);
            // Lock released successfully
            return true;
        } catch (Exception e) {
            log.error("DistributedRedisLock unlock [{}] Exception:", lockName, e);
            return false;
        }
    }

}

[LockTestController]

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Xiang
 * @date 2021/9/8 - 22:40
 */
@Slf4j
@RestController
@RequestMapping("/lock")
public class LockController {


    @Autowired
    DistributedRedisLock distributedRedisLock;

    AtomicInteger ID = new AtomicInteger(0);
    AtomicInteger ID1 = new AtomicInteger(0);

    // Test Do Not Release Lock
    @GetMapping("/testLock")
    public void testLock() {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
//                distributedRedisLock.lock(LOCK);
                try {
                    System.out.println(ID.addAndGet(1)+"Enter Wait");
                    cyclicBarrier.await();
                    System.out.println("Start execution");
                    post();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    // Test Do Not Release Lock
    @GetMapping("/testLock1")
    public void testLock1() {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
//                distributedRedisLock.lock(LOCK);
                try {
                    System.out.println(ID1.addAndGet(1)+"Enter Wait");
                    cyclicBarrier.await();
                    System.out.println("Start execution");
                    post1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    // How distributed locks are used in real-world business development
    public void post() throws InterruptedException {
        final String LOCK = "LOCK2LOCK";
        try {
            if (distributedRedisLock.lock(LOCK)) {
                log.info("No. e Two ready to start business logic");
                TimeUnit.SECONDS.sleep(1);
                // Business logic
                log.info("No. e Two Start Business Logics");
                TimeUnit.SECONDS.sleep(1);
            } else {
                // Handling logic that failed to acquire locks
                log.info("Failed to acquire lock");
            }
        } catch (Exception e) {
            log.error("Handle exceptions:", e);
        } finally {
            distributedRedisLock.unlock(LOCK);
            TimeUnit.SECONDS.sleep(1);
        }
    }


    // How distributed locks are used in real-world business development
    public void post1() throws InterruptedException {
        final String LOCK = "LOCK1LOCK";
        try {
            if (distributedRedisLock.lock(LOCK)) {
                // Business logic
                log.info("First Start Business Logic");
                TimeUnit.SECONDS.sleep(1);
            } else {
                // Handling logic that failed to acquire locks
                log.info("Failed to acquire lock");
            }
        } catch (Exception e) {
            log.error("Handle exceptions:", e);
        } finally {
            distributedRedisLock.unlock(LOCK);
            TimeUnit.SECONDS.sleep(1);
        }
    }

}

The test program here uses apache-jmeter-5.4.1 for performance pressure testing

Tags: Database Redis redisson

Posted on Wed, 08 Sep 2021 15:17:19 -0400 by Gamerz