Redis distributed lock solves the problem of repeated execution of timed tasks

Problem: The project is deployed to two servers and the timing tasks are repeatedly executed, causing data problems

Solution: Use the form of redis lock to solve, only one server is allowed to execute at a time

Reids lock + AOP aspect abstracts the locking part, and then uses the form of custom annotations to facilitate future locking of other places.

Above code:

Section class:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * redis 分布式锁
 */
@Aspect
@Slf4j
@Component
public class CacheLockAspect{

    @Resource
    private RedisUtil<String> redisUtil;
    /**
     * 分布式锁的key
     */
    private static final String KEY_PREFIX_LOCK = "CACHE_LOCK_ASPECT:";

    @Around("@annotation(com.xxx.xxxx.xxxx.CacheLock)")
    public void  cacheLockPoint(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method cacheMethod = signature.getMethod();
        if(null == cacheMethod){
            log.info("未获取到使用方法 pjp: {}",pjp);
            return;
        }
        String lockKey = cacheMethod.getAnnotation(CacheLock.class).lockedKey();
        Integer timeOut = cacheMethod.getAnnotation(CacheLock.class).expireTime();
        boolean release = cacheMethod.getAnnotation(CacheLock.class).release();
        if(StringUtils.isBlank(lockKey)){
            log.error("method:{}, 锁名称为空!",cacheMethod);
            return;
        }
        try {
            //这里使用jedis的setnx方法,不存在则添加,成功返回1,不成功返回0
            if (redisUtil.setnx(KEY_PREFIX_LOCK+lockKey, lockKey, timeOut).equals(1L)){
                //对这个key加上有效期
                redisUtil.expire(KEY_PREFIX_LOCK+lockKey, timeOut);
                log.info("method:{} 获得锁:{},开始运行!",Thread.currentThread(),KEY_PREFIX_LOCK+lockKey);
                pjp.proceed();
                return;
            }
            log.info("method:{} 未获取锁:{},运行失败!",Thread.currentThread(),KEY_PREFIX_LOCK+lockKey);
            //不需要释放锁
            release = false;
        } catch (Throwable e) {
            log.error("method:{},运行错误!",cacheMethod,e);
        }finally {
            if(release){
                log.info("method:{} 执行完成释放锁:{}",cacheMethod,KEY_PREFIX_LOCK+lockKey);
                redisUtil.delete(KEY_PREFIX_LOCK+lockKey);
            }
        }
    }
}

Custom annotations:

import java.lang.annotation.*;

/**自定义注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheLock {
    String lockedKey() default "";   //redis锁key
    int expireTime() default 100;    //key在redis里存在的时间 单位:秒
    boolean release() default false; //是否在方法执行完成之后释放锁
}

Test: Just add a custom annotation to the method that needs to be locked

//测试服务类
@Slf4j
@Component
public class TestServiceImpl {
	
    @CacheLock(lockedKey = "testLock", expireTime = 50)
    @Scheduled(cron = "0 0/1 * * * ? ")
    public void test1() {
        log.info("方法1开始执行!!!!");
    }
    @CacheLock(lockedKey = "testLock", expireTime = 50)
    @Scheduled(cron = "0 0/1 * * * ? ")
    public void test2() {
        log.info("方法2执行了====");
    }
}

Replenish: 

Redis tool class, implement setnx method

public class RedisUtil{

    @Autowired
    public RedisTemplate redisTemplate;


    public boolean setnx(String key, String val, Long expireTime){
        return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
            Boolean aBoolean = connection.setNX(key.getBytes(), val.getBytes());
            if (aBoolean){
                // 设置过期时间
                this.expire(key, expireTime);
                return true;
            }
            Long expireTime1 = this.getExpireTime(key);
            if (expireTime1 == -1L) {
                // 过期时间为-1, 删除该key
                this.deleteObject(key);
            }
            return false;
        });
    }
}

Reference article: Using Redis distributed locks to solve the problem of repeated execution of cluster server timing tasks

Guess you like

Origin blog.csdn.net/cccsssrrr/article/details/128168337