Explain how to implement distributed lock in springcloud distributed system in detail

  • 2021-12-11 17:47:50
  • OfStack

Directory 1. Introduction 2. Introduction to the redis command 3. Realize ideas 4. Encoding implementation Step 5 Pay attention 6. References

Recently, I have been looking at the distributed lock information, and I have seen the distributed lock chapter of Josial L "Redis in Action". The realization idea is to use springcloud combined with redis to realize distributed lock.

Note: There is something wrong with this article. Please see this article https://www.ofstack.com/article/228819. htm

1. Introduction

Generally speaking, when locking data, the program first obtains the lock through acquire to access the data exclusively, then performs some column operations on the data, and finally needs to release the lock. Redis itself is locked with the watch command, which is an optimistic lock. Using the watch command causes performance problems for frequently accessed keys.

2. Introduction to the redis command

SETNX Command (SET if Not eXists)
If and only if key does not exist, set the value of key to value and return 1; If the given key already exists, SETNX does nothing and returns 0.

SETEX Command
Set timeout time

GET Command
Returns the string value associated with key, or the special value nil if key does not exist.

DEL Command
Delete a given 1 or more key, and non-existent key will be ignored.

3. Realize ideas

Because the setnx command of redis is inherently suited to implement the locking function, this command only sets the value for the key if it does not exist. After acquiring the lock, other programs will fail to set the value again, that is, the lock cannot be acquired. Failed to acquire lock. Just keep trying to acquire the lock until the lock is successfully acquired, or until the set timeout is reached.

In addition, in order to prevent deadlock, that is, after a program acquires the lock, the program goes wrong and is not released, and other programs cannot acquire the lock, which leads to a series of problems and even leads to the system not running normally. At this time, it is necessary to set a timeout time for the lock, that is, setex command. After the lock timeout, other programs can acquire the lock.

4. Encoding implementation

This article uses springboot combined with redis to achieve, so you need to install an redis.

Firstly, the project of creating springboot is introduced, and redis is introduced.


<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>

  <!--  Open web-->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  
  <!-- redis-->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>

2. Create a lock class


/**
 *  Global lock, including the name of the lock 
 * Created by fangzhipeng on 2017/4/1.
 */
public class Lock {
    private String name;
    private String value;

    public Lock(String name, String value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }
}

3. The specific methods and ideas of creating distributed locks have been clearly stated, and the code comments have been written, so we will not explain them.


import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * Created by fangzhipeng on 2017/4/1.
 */
@Component
public class DistributedLockHandler {

    private static final Logger logger = LoggerFactory.getLogger(DistributedLockHandler.class);
    private final static long LOCK_EXPIRE = 30 * 1000L;// Time a single business holds a lock 30s Prevent deadlock 
    private final static long LOCK_TRY_INTERVAL = 30L;// Default 30ms Try 1 Times 
    private final static long LOCK_TRY_TIMEOUT = 20 * 1000L;// Default attempt 20s

    @Autowired
    private StringRedisTemplate template;

    /**
     *  Attempt to acquire global lock 
     *
     * @param lock  The name of the lock 
     * @return true  Get success, false Failed to get 
     */
    public boolean tryLock(Lock lock) {
        return getLock(lock, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
    }

    /**
     *  Attempt to acquire global lock 
     *
     * @param lock     The name of the lock 
     * @param timeout  Get the timeout   Unit ms
     * @return true  Get success, false Failed to get 
     */
    public boolean tryLock(Lock lock, long timeout) {
        return getLock(lock, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
    }

    /**
     *  Attempt to acquire global lock 
     *
     * @param lock         The name of the lock 
     * @param timeout      Get the timeout of the lock 
     * @param tryInterval  How many milliseconds to try to get 1 Times 
     * @return true  Get success, false Failed to get 
     */
    public boolean tryLock(Lock lock, long timeout, long tryInterval) {
        return getLock(lock, timeout, tryInterval, LOCK_EXPIRE);
    }

    /**
     *  Attempt to acquire global lock 
     *
     * @param lock            The name of the lock 
     * @param timeout         Get the timeout of the lock 
     * @param tryInterval     How many milliseconds to try to get 1 Times 
     * @param lockExpireTime  Expiration of lock 
     * @return true  Get success, false Failed to get 
     */
    public boolean tryLock(Lock lock, long timeout, long tryInterval, long lockExpireTime) {
        return getLock(lock, timeout, tryInterval, lockExpireTime);
    }


    /**
     *  Operation redis Getting a global lock 
     *
     * @param lock            The name of the lock 
     * @param timeout         The timeout obtained 
     * @param tryInterval     How much ms Try 1 Times 
     * @param lockExpireTime  Get the expiration time of the lock after success 
     * @return true  Get success, false Failed to get 
     */
    public boolean getLock(Lock lock, long timeout, long tryInterval, long lockExpireTime) {
        try {
            if (StringUtils.isEmpty(lock.getName()) || StringUtils.isEmpty(lock.getValue())) {
                return false;
            }
            long startTime = System.currentTimeMillis();
            do{
                if (!template.hasKey(lock.getName())) {
                    ValueOperations<String, String> ops = template.opsForValue();
                    ops.set(lock.getName(), lock.getValue(), lockExpireTime, TimeUnit.MILLISECONDS);
                    return true;
                } else {// Existence lock 
                    logger.debug("lock is exist! ! ! ");
                }
                if (System.currentTimeMillis() - startTime > timeout) {// Try to jump out of the loop directly after exceeding the set value 
                    return false;
                }
                Thread.sleep(tryInterval);
            }
            while (template.hasKey(lock.getName())) ;
        } catch (InterruptedException e) {
            logger.error(e.getMessage());
            return false;
        }
        return false;
    }

    /**
     *  Release lock 
     */
    public void releaseLock(Lock lock) {
        if (!StringUtils.isEmpty(lock.getName())) {
            template.delete(lock.getName());
        }
    }
}

4. Usage:


@Autowired
DistributedLockHandler distributedLockHandler;
Lock lock=new Lock("lockk","sssssssss);
if(distributedLockHandler.tryLock(lock){
 doSomething();
 distributedLockHandler.releaseLock();
}

Step 5 Pay attention

The setex command is used to prevent deadlock when using global locks, which requires setting the lock timeout time according to specific business. The other one is the granularity of locks. For example, in the actual combat of redis, in order to realize the trading function of the trading market, the whole trading market is locked, which leads to insufficient performance. The improvement scheme only locks the traded goods instead of the whole market.

6. References

Josiah. L "reids in action"

Implementation of Distributed Lock Based on Redis


Related articles: