SpringBoot+Zookeeper+Curator+SpEL+AOP实现优雅无侵入分布式锁_Dreamer_By的专栏-CSDN博客 - 亲测可用 - 但与实际场景有差别

SpringBoot+Zookeeper+Curator+SpEL+AOP实现优雅无侵入分布式锁

序言

分布式锁在主流实现技术上主要有以下三种:

  • 基于redis实现,比如redis官方推荐的redisson
  • 基于数据库实现,比如借助unique key
  • 基于zookeeper,主要是临时文件路径+session

至于优劣可参考另外一篇博客。本篇博客演示的是zookeeper结合apache的curator实现分布式锁。

maven依赖

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
    <exclusions>
        <!-- 防止zookeeper版本冲突 -->
        <exclusion>
            <artifactId>zookeeper</artifactId>
            <groupId>org.apache.zookeeper</groupId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
    <exclusions>
        <exclusion>
            <artifactId>zookeeper</artifactId>
            <groupId>org.apache.zookeeper</groupId>
        </exclusion>
    </exclusions>
</dependency>

配置文件

application.yml

zookeeper:
    servers: localhost:2181,localhost:2182 # 换成自己的环境
    lockPath: /lock/ # 指定分布式锁路径
    sessionTimeoutMs: 60000 # session超时时间
    connectionTimeoutMs: 5000 # 连接超时时间
    retryCount: 5 # 尝试获取锁最大重试次数
    elapsedTimeMs: 1000 # 重试间隔时间

配置类

@Data
@Component
@ConfigurationProperties(prefix = "zookeeper")
public class ZookeeperConfigurer {

    /** 尝试次数 */
    private int retryCount;

    /** 重试间隔时间 */
    private int elapsedTimeMs;

    /** session超时时间 */
    private int sessionTimeoutMs;

    /** 连接超时时间 */
    private int connectionTimeoutMs;

    /** zookeeper集群地址 */
    private String servers;

    /** zookeeper分布式锁跟路径 */
    private String lockPath;

}

分布式锁客户端

@Configuration
public class CuratorConfigurer {

    @Autowired
    private ZookeeperConfigurer zkConfigurer;

    @Bean(initMethod = "start", destroyMethod = "close")
    public CuratorFramework curatorFramework() {
        return CuratorFrameworkFactory.newClient(
                zkConfigurer.getServers(),
                zkConfigurer.getSessionTimeoutMs(),
                zkConfigurer.getConnectionTimeoutMs(),
                new RetryNTimes(zkConfigurer.getRetryCount(), zkConfigurer.getElapsedTimeMs()));
    }

}

分布式锁注解

/**
 * <p> 分布式锁注解 <p>
 * @author lamandonet@163.com
 * @version 1.0.0
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
    String name() ;
}

分布式锁切面类

@Aspect
@Component
public class DistributedLockAspect {
 
    @Autowired
    private CuratorFramework curatorFramework;
 
    @Pointcut("@annotation(com.clickpaas.annotation.DistributedLock)")
    public void distributedLockAspect() {}
 
    @Around(value = "distributedLockAspect()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        // target class
        Class<?> targetClass = pjp.getTarget().getClass();
        // method name
        String methodName = pjp.getSignature().getName();
        // parameter types
        Class<?>[] parameterTypes = ((MethodSignature) pjp.getSignature()).getMethod().getParameterTypes();
        // method
        Method method = targetClass.getMethod(methodName, parameterTypes);
        // arguments
        Object[] arguments = pjp.getArgs();

        return lock(pjp, method, arguments);
    }
    
    private Object lock(ProceedingJoinPoint pjp, Method method, Object[] arguments) throws Throwable {
        DistributedLock annotation = method.getAnnotation(DistributedLock.class);
        String lockName = parseKey( annotation, method, arguments );

        InterProcessMutex mutex = new InterProcessMutex(curatorFramework, zookeeperConfigurer.getLockPath() + lockName);
        if ( mutex.acquire(0, TimeUnit.SECONDS) ) {
            return pjp.proceed();
        }
        
        return null;
    }
    
    private String parseKey(DistributedLock annotation, Method method, Object[] args){
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String [] paraNameArr = u.getParameterNames(method);
        
        ExpressionParser parser = new SpelExpressionParser(); 
        StandardEvaluationContext context = new StandardEvaluationContext();
        
        for(int i=0; i<paraNameArr.length; ++i){
            context.setVariable(paraNameArr[i], args[i]);
        }
        
        String lockName = annotation.name();
        if(lockName.contains("#")) {
            lockName = parser.parseExpression(lockName).getValue(context, String.class);
        }

        return lockName;
    }
    
    @AfterThrowing(value = "distributedLockAspect()", throwing="ex")
    public void afterThrowing(Throwable ex) {
        throw new RuntimeException(ex);
    }
    
}

不好意思,不经常逛博客,不用手动调用,已经使用切面了肯定是在切面里面进行统一释放,demo比较粗糙,忘记写了。

InterProcessMutex mutex = new InterProcessMutex(curatorFramework, zookeeperConfigurer.getLockPath() + lockName);
if ( mutex.acquire(0, TimeUnit.SECONDS) ) {
    try{
        return pjp.proceed();
    } finally {
        mutex.release();
    }
}

原网址: 访问
创建于: 2020-12-03 16:56:03
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共2条评论
DeepMind

Rain酱

有参考意义 

2020-12-03 17:08:46 回复

DeepMind

Rain酱

https://deepmind.t-salon.cc/article/4646

2020-12-03 17:07:55 回复