提交 cd70fe1e 编写于 作者: 小傅哥's avatar 小傅哥

feat:Zookeeper 配种中心

上级
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
/xfg-dev-tech-app/data/
/data/
# 命令执行 docker-compose up -d
version: '3.1'
services:
zoo1:
image: zookeeper:3.9.0
container_name: zookeeper
restart: always
hostname: zoo1
ports:
- 2181:2181
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zoo1:2888:3888;2181
\ No newline at end of file
<mxfile host="Electron" modified="2023-08-31T03:10:20.410Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.2.8 Chrome/102.0.5005.167 Electron/19.0.15 Safari/537.36" etag="h9q0SDI69BR9USYSFyZc" version="20.2.8" type="device"><diagram id="vOU2qtN5xzDVvSGqbciK" name="第 1 页">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</diagram></mxfile>
\ No newline at end of file
zkCli.sh -server 192.168.1.103:2181
docker exec -it zookeeper bash
\ No newline at end of file
<?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>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-zookeeper</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>xfg-dev-tech-app</module>
<module>xfg-dev-tech-trigger</module>
<module>xfg-dev-tech-types</module>
</modules>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.28</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>xfg-dev-tech</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 统一设定POM版本信息插件 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.7</version>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<profileActive>dev</profileActive>
</properties>
</profile>
<profile>
<id>test</id>
<properties>
<profileActive>test</profileActive>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profileActive>prod</profileActive>
</properties>
</profile>
</profiles>
</project>
\ No newline at end of file
<?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>
<parent>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-zookeeper</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>xfg-dev-tech-app</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- 自身模块 -->
<dependency>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-trigger</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-types</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>xfg-dev-tech-app</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/**</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/**</include>
</includes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<skipTests>true</skipTests>
<testFailureIgnore>false</testFailureIgnore>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>cn.bugstack.xfg.dev.tech.Application</mainClass>
<layout>JAR</layout>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package cn.bugstack.xfg.dev.tech;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@Configurable
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package cn.bugstack.xfg.dev.tech.config;
import cn.bugstack.xfg.dev.tech.types.DCCValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* 基于 Zookeeper 的配置中心实现原理
*/
@Slf4j
@Configuration
public class DCCValueBeanFactory implements BeanPostProcessor {
private static final String BASE_CONFIG_PATH = "/xfg-dev-tech/config";
private final CuratorFramework client;
private final Map<String, Object> dccObjGroup = new HashMap<>();
public DCCValueBeanFactory(CuratorFramework client) throws Exception {
this.client = client;
// 节点判断
if (null == client.checkExists().forPath(BASE_CONFIG_PATH)) {
client.create().creatingParentsIfNeeded().forPath(BASE_CONFIG_PATH);
log.info("DCC 节点监听 base node {} not absent create new done!", BASE_CONFIG_PATH);
}
CuratorCache curatorCache = CuratorCache.build(client, BASE_CONFIG_PATH);
curatorCache.start();
curatorCache.listenable().addListener((type, oldData, data) -> {
switch (type) {
case NODE_CHANGED:
String dccValuePath = data.getPath();
Object objBean = dccObjGroup.get(dccValuePath);
try {
// 1. getDeclaredField 方法用于获取指定类中声明的所有字段,包括私有字段、受保护字段和公共字段。
// 2. getField 方法用于获取指定类中的公共字段,即只能获取到公共访问修饰符(public)的字段。
Field field = objBean.getClass().getDeclaredField(dccValuePath.substring(dccValuePath.lastIndexOf("/") + 1));
field.setAccessible(true);
field.set(objBean, new String(data.getData()));
field.setAccessible(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
break;
default:
break;
}
});
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = bean.getClass();
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(DCCValue.class)) {
DCCValue dccValue = field.getAnnotation(DCCValue.class);
try {
if (null == client.checkExists().forPath(BASE_CONFIG_PATH.concat("/").concat(dccValue.value()))) {
client.create().creatingParentsIfNeeded().forPath(BASE_CONFIG_PATH.concat("/").concat(dccValue.value()));
log.info("DCC 节点监听 listener node {} not absent create new done!", BASE_CONFIG_PATH.concat("/").concat(dccValue.value()));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
dccObjGroup.put(BASE_CONFIG_PATH.concat("/").concat(dccValue.value()), bean);
}
}
return bean;
}
}
package cn.bugstack.xfg.dev.tech.config;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(ZookeeperClientConfigProperties.class)
public class ZooKeeperClientConfig {
/**
* 多参数构建ZooKeeper客户端连接
*
* @return client
*/
@Bean(name = "zookeeperClient")
public CuratorFramework createWithOptions(ZookeeperClientConfigProperties properties) {
ExponentialBackoffRetry backoffRetry = new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(), properties.getMaxRetries());
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(properties.getConnectString())
.retryPolicy(backoffRetry)
.sessionTimeoutMs(properties.getSessionTimeoutMs())
.connectionTimeoutMs(properties.getConnectionTimeoutMs())
.build();
client.start();
return client;
}
}
package cn.bugstack.xfg.dev.tech.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "zookeeper.sdk.config", ignoreInvalidFields = true)
public class ZookeeperClientConfigProperties {
private String connectString;
private int baseSleepTimeMs;
private int maxRetries;
private int sessionTimeoutMs;
private int connectionTimeoutMs;
}
server:
port: 8091
application:
name: xfg-dev-tech-zookeeper
# zookeeper 自定义链接配置
zookeeper:
sdk:
config:
connect-string: 192.168.1.103:2181
base-sleep-time-ms: 1000
max-retries: 3
session-timeout-ms: 18000
connection-timeout-ms: 30000
# 日志
logging:
level:
root: debug
config: classpath:logback-spring.xml
\ No newline at end of file
server:
port: 8091
application:
name: xfg-dev-tech-zookeeper
# zookeeper 自定义链接配置
zookeeper:
sdk:
config:
connect-string: 10.253.6.71:2181
base-sleep-time-ms: 1000
max-retries: 3
session-timeout-ms: 1800000
connection-timeout-ms: 30000
# 日志
logging:
level:
root: info
config: classpath:logback-spring.xml
\ No newline at end of file
server:
port: 8091
application:
name: xfg-dev-tech-zookeeper
# zookeeper 自定义链接配置
zookeeper:
sdk:
config:
connect-string: 10.253.6.71:2181
base-sleep-time-ms: 1000
max-retries: 3
session-timeout-ms: 1800000
connection-timeout-ms: 30000
# 日志
logging:
level:
root: info
config: classpath:logback-spring.xml
\ No newline at end of file
spring:
config:
name: xfg-frame
profiles:
active: dev
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<springProperty scope="context" name="log.path" source="logging.path"/>
<!-- 日志格式 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>./data/log/log_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>./data/log/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>./data/log/log_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./data/log/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 日志文件保留天数【根据服务器预留,可自行调整】 -->
<maxHistory>7</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
<!-- WARN 级别及以上 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>
<!-- 异步输出 -->
<appender name="ASYNC_FILE_INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 队列剩余容量小于discardingThreshold,则会丢弃TRACT、DEBUG、INFO级别的日志;默认值-1,为queueSize的20%;0不丢失日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>8192</queueSize>
<!-- neverBlock:true 会丢失日志,但业务性能不受影响 -->
<neverBlock>true</neverBlock>
<!--是否提取调用者数据-->
<includeCallerData>false</includeCallerData>
<appender-ref ref="INFO_FILE"/>
</appender>
<appender name="ASYNC_FILE_ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 队列剩余容量小于discardingThreshold,则会丢弃TRACT、DEBUG、INFO级别的日志;默认值-1,为queueSize的20%;0不丢失日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>1024</queueSize>
<!-- neverBlock:true 会丢失日志,但业务性能不受影响 -->
<neverBlock>true</neverBlock>
<!--是否提取调用者数据-->
<includeCallerData>false</includeCallerData>
<appender-ref ref="ERROR_FILE"/>
</appender>
<!-- 开发环境:控制台打印 -->
<springProfile name="dev">
<logger name="com.nmys.view" level="debug"/>
</springProfile>
<root level="info">
<appender-ref ref="CONSOLE"/>
<!-- 异步日志-INFO -->
<appender-ref ref="ASYNC_FILE_INFO"/>
<!-- 异步日志-ERROR -->
<appender-ref ref="ASYNC_FILE_ERROR"/>
</root>
</configuration>
\ No newline at end of file
package cn.bugstack.xfg.dev.tech.test;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.CuratorEventType;
import org.apache.curator.framework.api.CuratorListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 单元测试
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApiTest {
@Resource
private CuratorFramework curatorFramework;
@Test
public void test_all() throws Exception {
String path = "/xfg-dev-tech/config/downgradeSwitch";
String data = "0";
curatorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(path, data.getBytes(StandardCharsets.UTF_8));
for (int i = 0; i < 2; i++) {
curatorFramework.setData().forPath(path, String.valueOf(i).getBytes(StandardCharsets.UTF_8));
}
}
/**
* 创建永久节点
*/
@Test
public void createNode() throws Exception {
String path = "/xfg-dev-tech/config/downgradeSwitch/test/a";
String data = "0";
if (null == curatorFramework.checkExists().forPath(path)) {
curatorFramework.create().creatingParentsIfNeeded().forPath(path);
}
}
/**
* 创建临时节点
*/
@Test
public void createEphemeralNode() throws Exception {
String path = "/xfg-dev-tech/config/epnode";
String data = "0";
curatorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(path, data.getBytes(StandardCharsets.UTF_8));
}
/**
* 创建临时有序节点
*/
@Test
public void crateEphemeralSequentialNode() throws Exception {
String path = "/xfg-dev-tech/config/epsnode";
String data = "0";
curatorFramework.create()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(path, data.getBytes(StandardCharsets.UTF_8));
}
/**
* 往节点种设置数据
*/
@Test
public void setData() throws Exception {
curatorFramework.setData().forPath("/xfg-dev-tech/config/downgradeSwitch", "111".getBytes(StandardCharsets.UTF_8));
curatorFramework.setData().forPath("/xfg-dev-tech/config/userWhiteList", "222".getBytes(StandardCharsets.UTF_8));
}
@Test
public void getData() throws Exception {
String downgradeSwitch = new String(curatorFramework.getData().forPath("/xfg-dev-tech/config/downgradeSwitch"), StandardCharsets.UTF_8);
log.info("测试结果: {}", downgradeSwitch);
String userWhiteList = new String(curatorFramework.getData().forPath("/xfg-dev-tech/config/userWhiteList"), StandardCharsets.UTF_8);
log.info("测试结果: {}", userWhiteList);
}
/**
* 异步修改数据
*/
@Test
public void setDataAsync() throws Exception {
String path = "/xfg-dev-tech/config/downgradeSwitch";
String data = "0";
CuratorListener listener = (client, event) -> {
Stat stat = event.getStat();
log.info("stat=" + JSON.toJSONString(stat));
CuratorEventType eventType = event.getType();
log.info("eventType=" + eventType.name());
};
curatorFramework.getCuratorListenable().addListener(listener);
curatorFramework.setData().inBackground().forPath(path, data.getBytes(StandardCharsets.UTF_8));
}
/**
* 删除节点
*/
@Test
public void deleteData() throws Exception {
String path = "/xfg-dev-tech/config/downgradeSwitch";
curatorFramework.delete().deletingChildrenIfNeeded().forPath(path);
}
/**
* 安全删除节点
*/
@Test
public void guaranteedDeleteData() throws Exception {
String path = "/xfg-dev-tech/config/downgradeSwitch";
curatorFramework.delete().guaranteed().forPath(path);
}
/**
* 获取子节点下的全部子节点路径集合
*/
@Test
public void watchedGetChildren() throws Exception {
String path = "/xfg-dev-tech";
List<String> children = curatorFramework.getChildren().watched().forPath(path);
log.info("测试结果:{}", JSON.toJSONString(children));
}
/**
* 获取节点数据
*/
@Test
public void getDataByPath() throws Exception {
String path = "/xfg-dev-tech/config/downgradeSwitch";
String fullClassName = "";
String jsonStr = new String(curatorFramework.getData().forPath(path), StandardCharsets.UTF_8);
Class clazz = Class.forName(fullClassName);
log.info("测试结果:{}", JSON.parseObject(jsonStr, clazz));
}
}
<?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>
<parent>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-zookeeper</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>xfg-dev-tech-trigger</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-types</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>xfg-dev-tech-trigger</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerVersion>${java.version}</compilerVersion>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package cn.bugstack.xfg.dev.tech.trigger.http;
import cn.bugstack.xfg.dev.tech.types.DCCValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.transaction.CuratorOp;
import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
import org.apache.curator.framework.api.transaction.TransactionOp;
import org.apache.zookeeper.KeeperException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Slf4j
@RestController
public class ConfigController {
@DCCValue("downgradeSwitch")
private String downgradeSwitch;
@DCCValue("userWhiteList")
private String userWhiteList;
@Resource
private CuratorFramework curatorFramework;
/**
* curl http://localhost:8091/getConfig/downgradeSwitch
*/
@RequestMapping("/getConfig/downgradeSwitch")
public String getConfigDowngradeSwitch() {
return downgradeSwitch;
}
/**
* curl http://localhost:8091/getConfig/userWhiteList
*/
@RequestMapping("/getConfig/userWhiteList")
public String getConfigUserWhiteList() {
return userWhiteList;
}
/**
* curl -X GET "http://localhost:8091/setConfig?downgradeSwitch=true&userWhiteList=xfg,xfg2"
*/
@GetMapping("/setConfig")
public void setConfig(Boolean downgradeSwitch, String userWhiteList) throws Exception {
curatorFramework.setData().forPath("/xfg-dev-tech/config/downgradeSwitch", (downgradeSwitch ? "开" : "关").getBytes(StandardCharsets.UTF_8));
curatorFramework.setData().forPath("/xfg-dev-tech/config/userWhiteList", userWhiteList.getBytes(StandardCharsets.UTF_8));
}
/**
* 事务操作
* curl -X GET "http://localhost:8091/setConfigExecuteTranSanction?downgradeSwitch=false&userWhiteList=xfg,user2,user3"
*/
@GetMapping("/setConfigExecuteTranSanction")
public List<CuratorTransactionResult> setConfigExecuteTranSanction(Boolean downgradeSwitch, String userWhiteList) throws Exception {
TransactionOp transactionOp = curatorFramework.transactionOp();
CuratorOp downgradeSwitchCuratorOp = transactionOp.setData().forPath("/xfg-dev-tech/config/downgradeSwitch", (downgradeSwitch ? "开" : "关").getBytes(StandardCharsets.UTF_8));
CuratorOp userWhiteListCuratorOp = transactionOp.setData().forPath("/xfg-dev-tech/config/userWhiteList", userWhiteList.getBytes(StandardCharsets.UTF_8));
List<CuratorTransactionResult> transactionResults = curatorFramework.transaction().forOperations(downgradeSwitchCuratorOp, userWhiteListCuratorOp);
for (CuratorTransactionResult transactionResult : transactionResults) {
log.info("事务结果:{} - {}", transactionResult.getForPath(), transactionResult.getType());
}
return transactionResults;
}
/**
* 测试方法
* @throws InterruptedException
* @throws KeeperException
*/
public void lock() throws InterruptedException, KeeperException {
DistributedLock lock = new DistributedLock(curatorFramework, "/xfg-dev-tech/config");
try {
lock.lock();
// 执行需要加锁的代码
lock.unlock();
} catch (Exception e) {
e.printStackTrace();
} finally {
curatorFramework.close();
}
}
}
package cn.bugstack.xfg.dev.tech.trigger.http;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
public class DistributedLock {
private CuratorFramework curatorFramework;
private String lockPath;
private InterProcessMutex mutex;
private InterProcessSemaphoreMutex semaphoreMutex;
private InterProcessReadWriteLock readWriteLock;
public DistributedLock(CuratorFramework curatorFramework, String lockPath) {
this.curatorFramework = curatorFramework;
this.lockPath = lockPath;
this.mutex = new InterProcessMutex(curatorFramework, lockPath);
this.semaphoreMutex = new InterProcessSemaphoreMutex(curatorFramework, lockPath);
this.readWriteLock = new InterProcessReadWriteLock(curatorFramework, lockPath);
}
public void lock() throws Exception {
mutex.acquire();
}
public void unlock() throws Exception {
mutex.release();
}
public void semaphoreLock() throws Exception {
semaphoreMutex.acquire();
}
public void semaphoreUnlock() throws Exception {
semaphoreMutex.release();
}
public void readLock() throws Exception {
readWriteLock.readLock().acquire();
}
public void readUnlock() throws Exception {
readWriteLock.readLock().release();
}
public void writeLock() throws Exception {
readWriteLock.writeLock().acquire();
}
public void writeUnlock() throws Exception {
readWriteLock.writeLock().release();
}
}
\ No newline at end of file
package cn.bugstack.xfg.dev.tech.trigger.listener;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Slf4j
@Component
public class ZooKeeperListener {
@Resource
private CuratorFramework zkClient;
@PostConstruct
public void init() throws Exception {
// 保证实时性,利用zk的watch机制
CuratorCache curatorCache = CuratorCache.build(zkClient, "/xfg-dev-tech");
curatorCache.start();
// 创建监听器
curatorCache.listenable().addListener((type, oldData, data) -> {
switch (type) {
case NODE_CHANGED:
// 1. 获取变更节点的路径名
String configName = data.getPath().replace("/xfg-dev-tech/", "");
// 监听到zk的zNode发生了数据变更
log.info("节点变更 node:{} data: {}", configName, new String(data.getData()));
break;
default:
break;
}
});
}
}
<?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>
<parent>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-zookeeper</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>xfg-dev-tech-types</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
\ No newline at end of file
package cn.bugstack.xfg.dev.tech.types;
import java.lang.annotation.*;
/**
* 注解,动态配置中心
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Documented
public @interface DCCValue {
String value() default "";
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册