Springboot分布式锁实践(redis方案)

概述

为了保证一个方法在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或synchronized)进行互斥控制。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。
本方案将使用redis单线程的特性,将锁存放在redis中,多线程从redis中抢占这个锁。

引入依赖

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

redis属性配置

spring:
  redis:
    host: 192.168.1.200
    password: 123123
    port: 6379
    database: 1

创建锁注解

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * 锁的注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LockAop {

    /**
     * redis 锁key
     *
     * @return redis 锁key
     */
    String lockKey() default "";

    /**
     * 过期秒数,默认为5秒
     *
     * @return 轮询锁的时间
     */
    int expire() default 5;

    /**
     * 超时时间单位
     *
     * @return 秒
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

}

锁拦截器(AOP)

import com.demo.annotation.LockAop ;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * 锁拦截器
 */
@Aspect
@Configuration
public class LockAspectInterceptor {

    @Autowired
    public LockMethodInterceptor(StringRedisTemplate lockRedisTemplate, CacheKeyGenerator cacheKeyGenerator) {
        this.lockRedisTemplate = lockRedisTemplate;
        this.cacheKeyGenerator = cacheKeyGenerator;
    }

    private final StringRedisTemplate lockRedisTemplate;
    private final CacheKeyGenerator cacheKeyGenerator;


    @Around("execution(public * *(..)) && @annotation(com.demo.annotation.LockAop )")
    public Object interceptor(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        LockAop lock = method.getAnnotation(LockAop.class);
        if (StringUtils.isEmpty(lock.getLocakKey())) {
            throw new RuntimeException("lock key can't be null...");
        }
         //setIfAbsent方法特性:key不存在才能设置成功,返回true,存在则不设置,返回false
         final Boolean success = lockRedisTemplate.opsForValue().setIfAbsent(lock.getLocakKey(), "lockKey");
            if (success) {
                lockRedisTemplate.expire(lock.getLocakKey(), lock.expire(), lock.timeUnit());
            } else {
                //可抛出自定义 异常;
                throw new RuntimeException("当前资源已被抢占,请稍后重试!");
            }
        try {
            try {
               //保证了该逻辑方法当前仅有一个线程在处理
                return pjp.proceed();
            } catch (Throwable throwable) {
                throw new RuntimeException("系统异常");
            }
        } finally {
            //处理完毕,释放锁
            lockRedisTemplate.delete(lockKey);
        }
    }
}

service层

/**
*测试锁,抢占
*/
@Service
public class testService{
	//expire:为避免锁过期时间太早,演示的时候可设置为60s,生成环境一般设置小一点
	@LockAop(lockKey="lock:test",expire=60)
	public String getLock(){
		try {
		           //为方便演示,线程拿到锁进入该方法后,先睡眠60秒,在这60s内,其他线程(请求)都拿不到锁
                    Thread.sleep(60);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
		return "已拿到锁";
	}
}

然后在写个controller调用该方法即可测试啦,这里就不具体实现了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值