1. 程式人生 > 其它 >L3-003 社交叢集

L3-003 社交叢集

此 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("請勿重複提交");
            }