SpringBoot integration Redis distributed lock tutorial

From today on, roll up your sleeves and follow me


preface

Using Spring Boot makes it very easy and fast to set up projects, so we don’t have to worry about compatibility between frameworks, applicable versions, etc. We can use anything we want, just add a configuration.


Tip: The following is the body of this article, the following cases for reference

I. Technical introduction

1. What is Redis?

Redis is a high performance key-value database. The appearance of Redis largely compensates for the deficiency of memcached key/value storage. In some cases, Redis can play a good supplement to relational database.

2. What is a distributed lock?

Distributed lock is a distributed coordination technique to prevent interference between multiple processes in distributed system.

Two, the use of steps

1. Import maven libraries

The code is as follows (example) :

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.41.</version>
          <relativePath/>
      </parent>
       <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.41.</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.41.</version>
        </dependency>
    </dependencies>
Copy the code

2. Encapsulate the Redis distributed lock utility class

The code is as follows (example) :

package com.hyh.redis.helper;

import com.hyh.utils.common.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;

/** * redis distributed lock assistant ** @author: heyuhua * @date: 2020/1/6 17:15 */
@Component
public class RedisDistributionLockHelper {

    /** * log */
    private static final Logger LOG = LoggerFactory.getLogger(RedisDistributionLockHelper.class);
    /*** distributed lock key ***/
    private static final String DISTRIBUTION_LOCK_KEY = "distribut_lock_";
    /*** Distributed lock expiration time ***/
    private static final Integer EXPIRE_TIME = 30;
    /*** Each spin sleep time ***/
    private static final Integer SLEEP_TIME = 50;
    /*** Number of spins of distributed lock ***/
    private static final Integer CYCLES = 10;
    /** * Redis template calls class */
    @Autowired
    private RedisTemplate redisTemplate;

    /** * initializes */
    @PostConstruct
    public void init(a) {
        LOG.info("-- Redis distributed lock assistant initialization ----");
    }

    /** * @param key unique identifier for locking * @param value Unique identifier for unlocking locks (it is recommended to use thread ID as value) */
    public void lock(String key, String value) {
        lock(key, value, EXPIRE_TIME);
    }

    /** * @param key Unique identifier for locking * @param Value Unique identifier for unlocking the lock (it is recommended to use the thread ID as the value) * @param timeout Timeout period (unit: S) */
    public void lock(String key, String value, Integer timeout) {
        Assert.isTrue(StringUtils.isNotBlank(key), "redis locks are identified as null.");
        Assert.isTrue(StringUtils.isNotBlank(value), "the redis release lock is identified as null.");
        int cycles = CYCLES;
        // ----- attempts to acquire the lock. If the lock is obtained, return directly. Otherwise, loop attempts to obtain the lock
        while(! tryLock(key, value, timeout)) {// ----- loop up to 10 times. If no lock is obtained after 10 attempts, throw an exception
            if (0 == (cycles--)) {
                LOG.error("redis try lock fail. key: {}, value: {}", key, value);
                throw new RuntimeException("redis try lock fail.");
            }
            try {
                TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
            } catch (Exception e) {
                LOG.error("history try lock error.", e); }}}/** * @param key unique identifier for locking * @param value Unique identifier for unlocking the lock (thread ID is recommended as the value) * @param timeout Timeout period (unit: S) * @return [true: False: lock failed] */
    private boolean tryLock(String key, String value, Integer timeout) {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(DISTRIBUTION_LOCK_KEY + key, value, timeout, TimeUnit.SECONDS);
        returnresult ! = null && result.booleanValue(); }/** * release lock ** @param key Unique identifier of lock release * @param value Unique identifier of lock release (it is recommended to use thread ID as value) */
    public void unLock(String key, String value) {
        Assert.isTrue(StringUtils.isNotBlank(key), "redis locks are identified as null.");
        Assert.isTrue(StringUtils.isNotBlank(value), "the redis release lock is identified as null.");
        key = DISTRIBUTION_LOCK_KEY + key;
        ----- Check whether the lock is the lock by value. If yes, release the lock. If no, do not release the data to avoid accidental deletion
        if (value.equals(redisTemplate.opsForValue().get(key))) {
            redisTemplate.opsForValue().getOperations().delete(key); }}/** * cancel */
    @PreDestroy
    public void destory(a) {
        LOG.info("-- Redis Distributed lock Assistant logout ----"); }}Copy the code

3. Configuration file

The code is as follows (example) :

server:
  port: 8088
spring:
  # redis configuration
  redis:
    host: 192.1686.134.
    port: 30511
    password:
    jedis:
      pool:
        max-active: 8
        max-wait: - 1
        max-idle: 500
        min-idle: 0
    lettuce:
      shutdown-timeout: 0
Copy the code

Unit testing

The code is as follows (example) :

@Autowired
    private RedisDistributionLockHelper redisDistributionLockHelper;

    /** * Test distributed Redis lock */
    @Test
    public void testLock(a) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100.100.20, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200));
        int idx = 100;
        while (--idx > 0) {
            threadPoolExecutor.execute(() -> {
                try {
                    redisDistributionLockHelper.lock("hyh", Thread.currentThread().getId() + "");
                    // Only one thread has successfully acquired the lock
                    System.out.println("-- Succeeded in obtaining lock, current thread ID is [" + Thread.currentThread().getId() + "】 -");
                } catch (RuntimeException runtimeException) {
                    System.out.println("-- Failed to acquire lock, current thread ID is [" + Thread.currentThread().getId() + "】 -"); }});try {
                TimeUnit.MILLISECONDS.sleep(50);
            } catch (Exception e) {
                System.err.println("error:"+ e.getMessage()); }}// Close the thread pool manually when using local variables
        threadPoolExecutor.shutdown();
    }
Copy the code

conclusion

Doesn’t it feel easy? Follow me to reveal more Redis advanced usage source address: click here to view the source code.