SpringBoot分布式锁之redis实现

一、pom文件配置

1 <dependency>
2     <groupId>org.springframework.boot</groupId>
3     <artifactId>spring-boot-starter-data-redis</artifactId>
4 </dependency>
5                

 

二、yml文件配置

spring:
  redis:
    host: 192.168.80.88
    port: 6379
    password:
    database: 5
    timeout: 2000ms
    jedis:
      pool:
        max-idle: 20
        max-wait: 1000ms
        max-active: 100

  

三、启动配置

 1 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 2 import org.springframework.context.annotation.Bean;
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.core.Ordered;
 5 
 6 import java.util.Collections;
 7 import java.util.List;
 8 
 9 
10 @Configuration
11 public class RedisDistributedLockConfig {
12 
13     /**
14      * 添加RequestId
15      * @return
16      */
17     @Bean
18     public FilterRegistrationBean requestIdFilter() {
19         RequestIdFilter reqestIdFilter = new RequestIdFilter();
20         FilterRegistrationBean registrationBean = new FilterRegistrationBean();
21         registrationBean.setFilter(reqestIdFilter);
22         List<String> urlPatterns = Collections.singletonList("/*");
23         registrationBean.setUrlPatterns(urlPatterns);
24         registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
25         return registrationBean;
26     }
27 }

 

四、其它配置

1、注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Description //标注在方法上的分布式锁注解
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DistributedLockAble {
    String key();

    String prefix() default "disLock:";

    long expire() default 10L; // 默认10s过期
}

 

2、注解aop

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @Description //分布式锁注解处理切面
 **/
@Aspect
@Slf4j
@Component
public class DistributedLockAspect {

    @Autowired
    private RedisUtils lock;

    public DistributedLockAspect(RedisUtils lock) {
        this.lock = lock;
    }

    /**
     * 在方法上执行同步锁
     */
    @Around(value = "@annotation(lockable)")
    public Object distLock(ProceedingJoinPoint point, DistributedLockAble lockable) throws Throwable {
        boolean locked = false;
        String key = lockable.prefix() + lockable.key();
        try {
            locked = lock.lock(key, WebUtil.getRequestId(), lockable.expire());
            if(locked) {
                return point.proceed();
            } else {
                log.error("Did not get a lock for key {}", key);
                throw new RuntimeException("分布式锁获取失败");
            }
        } catch (Exception e) {
            throw e;
        } finally {
            if(locked) {
                if(!lock.unLock(key, WebUtil.getRequestId())){
                    log.warn("Unlock {} failed, maybe locked by another client already. ", lockable.key());
                }
            }
        }
    }
}

 

3、redis工具类

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Description //redis工具类
 **/
@Component
@Slf4j
public class RedisUtils {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    private static final Long RELEASE_LOCK_SUCCESS_RESULT = 1L;
    private long waitMillisPer = 1000L;

    public void cacheValue(String key, String value){
        redisTemplate.opsForValue().set(key, value);
    }
public String getValueByKey(String key){ return redisTemplate.opsForValue().get(key); } public void deleteByKey(String key){ redisTemplate.delete(key); } /** * 尝试获取锁(立即返回) * @param key 锁的redis key * @param value 锁的value * @param expire 过期时间/秒 * @return 是否获取成功 */ public boolean lock(String key, String value, long expire) { return redisTemplate.opsForValue().setIfAbsent(key, value, expire, TimeUnit.SECONDS); } /** * 尝试获取锁,并至多等待timeout时长 * * @param key 锁的redis key * @param value 锁的value * @param expire 过期时间/秒 * @param timeout 超时时长 * @param unit 时间单位 * @return 是否获取成功 */ public boolean lock(String key, String value, long expire, long timeout, TimeUnit unit) { long waitMillis = unit.toMillis(timeout); long waitAlready = 0; while (!redisTemplate.opsForValue().setIfAbsent(key, value, expire, TimeUnit.SECONDS) && waitAlready < waitMillis) { try { Thread.sleep(waitMillisPer); } catch (InterruptedException e) { log.error("Interrupted when trying to get a lock. key: {}", key, e); } waitAlready += waitMillisPer; } if (waitAlready < waitMillis) { return true; } log.warn("<====== lock {} failed after waiting for {} ms", key, waitAlready); return false; } /** * 释放锁 * @param key 锁的redis key * @param value 锁的value */ public boolean unLock(String key, String value) { DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT, Long.class); long result = redisTemplate.execute(redisScript, Collections.singletonList(key), value); return Objects.equals(result, RELEASE_LOCK_SUCCESS_RESULT); } }

 

4、其它工具类

import java.util.UUID;

/**
 * @Description //请求id管理
 **/
public class WebUtil {
    public static final String REQ_ID_HEADER = "Req-Id";

    private static final ThreadLocal<String> REQ_ID_THREAD_LOCAL = new ThreadLocal<>();

    public static void setRequestId(String requestId) {
        REQ_ID_THREAD_LOCAL.set(requestId);
    }

    public static String getRequestId(){
        String requestId = REQ_ID_THREAD_LOCAL.get();
        if(requestId == null) {
            requestId = getUid();
            REQ_ID_THREAD_LOCAL.set(requestId);
        }
        return requestId;
    }

    public static void removeRequestId() {
        REQ_ID_THREAD_LOCAL.remove();
    }

    public static String getUid(){
        return UUID.randomUUID().toString().toUpperCase().replaceAll("-", "");
    }
}

 

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Description //请求id拦截器
 **/
public class RequestIdFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String reqId = httpServletRequest.getHeader(WebUtil.REQ_ID_HEADER);
        //没有则生成一个
        if (StringUtils.isEmpty(reqId)) {
            reqId = WebUtil.getUid();
        }
        WebUtil.setRequestId(reqId);
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            WebUtil.removeRequestId();
        }
    }

}

 

五、使用

   @DistributedLockAble(key = "distributed_lock", expire = 100)
    public void test_method(String key){
        // TODO
    }

 

六、打完收工~

posted @ 2020-07-18 15:14  时の封印  阅读(281)  评论(0编辑  收藏  举报