zookeeper分布式锁
此 demo 主要演示了如何使用 Spring Boot 集成 Zookeeper 结合AOP实现分布式锁。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-zookeeper</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-demo-zookeeper</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- curator 版本4.1.0 对应 zookeeper 版本 3.5.x -->
<!-- curator 与 zookeeper 版本对应关系:https://curator.apache.org/zk-compatibility.html -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-zookeeper</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ZkProps.java
/**
* <p>
* Zookeeper 配置项
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-27 14:47
*/
@Data
@ConfigurationProperties(prefix = "zk")
public class ZkProps {
/**
* 连接地址
*/
private String url;
/**
* 超时时间(毫秒),默认1000
*/
private int timeout = 1000;
/**
* 重试次数,默认3
*/
private int retry = 3;
}
application.yml
server:
port: 8080
servlet:
context-path: /demo
zk:
url: 127.0.0.1:2181
timeout: 1000
retry: 3
ZkConfig.java
/**
* <p>
* Zookeeper配置类
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-27 14:45
*/
@Configuration
@EnableConfigurationProperties(ZkProps.class)
public class ZkConfig {
private final ZkProps zkProps;
@Autowired
public ZkConfig(ZkProps zkProps) {
this.zkProps = zkProps;
}
@Bean
public CuratorFramework curatorFramework() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(zkProps.getTimeout(), zkProps.getRetry());
CuratorFramework client = CuratorFrameworkFactory.newClient(zkProps.getUrl(), retryPolicy);
client.start();
return client;
}
}
ZooLock.java
/** * <p> * 基于Zookeeper的分布式锁注解 * 在需要加锁的方法上打上该注解后,AOP会帮助你统一管理这个方法的锁 * </p> * * @author yangkai.shen * @date Created in 2018-12-27 14:11 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface ZooLock { /** * 分布式锁的键 */ String key(); /** * 锁释放时间,默认五秒 */ long timeout() default 5 * 1000; /** * 时间格式,默认:毫秒 */ TimeUnit timeUnit() default TimeUnit.MILLISECONDS; }
LockKeyParam.java
/** * <p> * 分布式锁动态key注解,配置之后key的值会动态获取参数内容 * </p> * * @author yangkai.shen * @date Created in 2018-12-27 14:17 */ @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface LockKeyParam { /** * 如果动态key在user对象中,那么就需要设置fields的值为user对象中的属性名可以为多个,基本类型则不需要设置该值 * <p>例1:public void count(@LockKeyParam({"id"}) User user) * <p>例2:public void count(@LockKeyParam({"id","userName"}) User user) * <p>例3:public void count(@LockKeyParam String userId) */ String[] fields() default {}; }
ZooLockAspect.java
/**
* <p>
* 使用 aop 切面记录请求日志信息
* </p>
*
* @author yangkai.shen
* @date Created in 2018-10-01 22:05
*/
@Aspect
@Component
@Slf4j
public class ZooLockAspect {
private final CuratorFramework zkClient;
private static final String KEY_PREFIX = "DISTRIBUTED_LOCK_";
private static final String KEY_SEPARATOR = "/";
@Autowired
public ZooLockAspect(CuratorFramework zkClient) {
this.zkClient = zkClient;
}
/**
* 切入点
*/
@Pointcut("@annotation(com.xkcoding.zookeeper.annotation.ZooLock)")
public void doLock() {
}
/**
* 环绕操作
*
* @param point 切入点
* @return 原方法返回值
* @throws Throwable 异常信息
*/
@Around("doLock()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Object[] args = point.getArgs();
ZooLock zooLock = method.getAnnotation(ZooLock.class);
if (StrUtil.isBlank(zooLock.key())) {
throw new RuntimeException("分布式锁键不能为空");
}
String lockKey = buildLockKey(zooLock, method, args);
InterProcessMutex lock = new InterProcessMutex(zkClient, lockKey);
try {
// 假设上锁成功,以后拿到的都是 false
if (lock.acquire(zooLock.timeout(), zooLock.timeUnit())) {
return point.proceed();
} else {
throw new RuntimeException("请勿重复提交");
}