Backend——" Use redis distributed lock to solve the problem of repeated execution of timing tasks in a cluster environment

In a cluster environment, the timing tasks of non-idempotent operations can usually only be executed once. But the program is distributed on each server and runs. At this time, redis distributed lock can be used to ensure that the timing task can only be executed once. The following is a demo:

 I started two programs locally, each of which contains a timing task with exactly the same content to simulate a distributed environment.

It can be seen that with a frequency of one round per minute, it can be seen that only one program is being executed for each round of timing tasks.

The specific logic is to add operations to determine whether to execute business logic in the timing task. The criterion for this judgment is achieved through redis+key:

Before the business logic is executed, the setIfAbsent method is used to determine whether there is a key in redis. If there is no key, the key is set. If the setting succeeds, it returns true. At this time, the key is in an unlocked state, and if it contains a key, it returns false.

The timing task code is as follows:

import com.baoji.service.DistributeLocker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.ParseException;

/**
 * Created by dearx on 2021/01/21.
 */
@Component
public class TestGenerater1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestGenerater1.class);

    @Autowired
    private DistributeLocker distributeLocker;

    //每隔1分钟执行一次
    @Scheduled(cron = "0 */1 * * * ?")
    private void configureTasks() throws ParseException {
        boolean locked = distributeLocker.lock("test-DingShiRengWu", distributeLocker.LOCK_TIME_ONE_HOUR);
        if(!locked){
            LOGGER.info("定时任务aaa已经被执行");
            distributeLocker.unlock("test-DingShiRengWu");
        }else{
            LOGGER.info("定时任务aaa开始执行");
            distributeLocker.lock("test-DingShiRengWu");
            try {
                Thread.currentThread().sleep(5000);
                /*业务逻辑处理*/
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            distributeLocker.unlock("test-DingShiRengWu");
            LOGGER.info("定时任务aaa执行完毕");
        }
        LOGGER.info("----------------这一轮执行结束,开始下一轮-------------");
    }

}

The redis related code is as follows:

import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;


@Component
public class DistributeLocker {

  private static final Logger LOGGER = LoggerFactory.getLogger(DistributeLocker.class);

  private static final String LOCK = "TRUE";

  private static final long DEFAULT_LOCK_TIME = 60000L;
  
  public static final long LOCK_TIME_ONE_HOUR = 60 * 60000L;

  public static final long LOCK_TIME_HALF_HOUR = 30 * 60000L;

  public static final long LOCK_TIME_FIVE_MINS = 5 * 60000L;
  
  @Autowired
  private RedisTemplate<String, String> stringRedisTemplate;

  /**
   * lock the operation with the default time, the default lock time is 60 seconds
   * @param key the given key
   * @return
   */
  public boolean lock(String key) {
    return lock(key, DEFAULT_LOCK_TIME);
  }
  
  /**
   * lock the operation with the given key
   * @param key the given key
   * @param lockTime unit is milliseconds, the lock time should greater than estimated operation time
   * @return true if lock success
   */
  public boolean lock(String key, long lockTime) {
    BoundValueOperations<String, String> operations = stringRedisTemplate.boundValueOps(key);
    boolean lockSuccess = operations.setIfAbsent(LOCK);
    if(lockSuccess) {
      operations.expire(lockTime, TimeUnit.MILLISECONDS);
    }

    return lockSuccess;

//    RedisConnection connection = null;
//    try {
//      connection = stringRedisTemplate.getConnectionFactory().getConnection();
//      lockSuccess = connection.setNX(key.getBytes(Charset.forName("UTF-8")), LOCK.getBytes(Charset.forName("UTF-8")));
//      if(lockSuccess) {
//        connection.expire(key.getBytes(Charset.forName("UTF-8")), lockTime);
//      }
//    } finally {
//      connection.close();
//    }
  }

  /**
   * unlock the operation with the given key
   * @param key
   */
  public void unlock(String key) {
    stringRedisTemplate.delete(key);
  }

}

 

Guess you like

Origin blog.csdn.net/nienianzhi1744/article/details/112994335