L3-003 社交叢集
阿新 • • 發佈:2022-04-19
此 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("請勿重複提交"