From 88cd1c51c2d7bcbf4fa516e18a02949a15e38165 Mon Sep 17 00:00:00 2001 From: odboy Date: Wed, 11 Dec 2024 20:56:47 +0800 Subject: [PATCH] Signed-off-by: odboy --- README.md | 114 ++++---- doc/d20241210215050.png | Bin 0 -> 166628 bytes .../cn/odboy/config/context/CallBack.java | 27 -- .../config/context/SpringContextHolder.java | 140 ---------- .../cn/odboy/config/model/SmallMessage.java | 95 ++++--- .../config/model/msgtype/ClientInfo.java | 25 ++ .../config/model/msgtype/ConfigFileInfo.java | 8 + .../cn/odboy/config/util/ChannelUtil.java | 26 +- .../cn/odboy/config/util/MessageUtil.java | 43 ++- .../cn/odboy/config/util/PropertiesUtil.java | 264 ++++++++++++++++++ .../odboy/config/util/PropertyNameUtil.java | 7 + .../cn/odboy/config/util/ProtostuffUtil.java | 103 ++++--- .../config/context/ClientConfigLoader.java | 38 ++- .../config/context/ClientPropertyHelper.java | 84 ++++++ .../context/ClientPropertyRefresher.java | 72 ----- .../java/cn/odboy/rest/DemoController.java | 6 +- .../main/java/cn/odboy/domain/ConfigApp.java | 4 +- .../odboy/infra/netty/ConfigClientManage.java | 195 +++++++------ .../service/impl/ConfigAppEnvServiceImpl.java | 6 +- .../service/impl/ConfigFileServiceImpl.java | 17 +- kenaito_config.sql | 10 +- 21 files changed, 783 insertions(+), 501 deletions(-) create mode 100644 doc/d20241210215050.png delete mode 100644 kenaito-config-common/src/main/java/cn/odboy/config/context/CallBack.java delete mode 100644 kenaito-config-common/src/main/java/cn/odboy/config/context/SpringContextHolder.java create mode 100644 kenaito-config-common/src/main/java/cn/odboy/config/util/PropertiesUtil.java create mode 100644 kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyHelper.java delete mode 100644 kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyRefresher.java diff --git a/README.md b/README.md index d474b89..51837d9 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,80 @@ -

Kenaito Config

+# Kenaito Config -## 项目简介 +## 简介 -基于 Spring Boot 2.7.18 、 Mybatis-Plus、 JWT、Spring Security、Redis、Vue 的 配置中心 +Kenaito Config 是一个轻量级的配置中心,旨在简化应用配置,使其更加优雅。它专为自研 DevOps 平台设计。 -**账号密码:** `admin / 123456` +## 技术栈 + +- **后端**: + - Spring Boot 2.7.18 + - Mybatis-Plus + - JWT + - Spring Security + - Redis +- **前端**: + - Vue 2 ## 系统功能 -- 用户管理:提供用户的相关配置,新增用户后,默认密码为123456 -- 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限 -- 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单 -- 部门管理:可配置系统组织架构,树形表格展示 -- 岗位管理:配置各个部门的职位 -- 字典管理:可维护常用一些固定的数据,如:状态,性别等 -- SQL监控:采用druid 监控数据库访问性能,默认用户名admin,密码123456 -- 邮件工具:配合富文本,发送html格式的邮件 -- 服务监控:监控服务器的负载情况 +- **用户管理**: 提供用户相关配置,新增用户默认密码为 `123456`。 +- **角色管理**: 分配权限与菜单,支持按部门设置角色数据权限。 +- **菜单管理**: 实现菜单动态路由,支持多级菜单配置。 +- **部门管理**: 配置系统组织架构,以树形表格展示。 +- **岗位管理**: 配置各部门职位。 +- **字典管理**: 维护常用固定数据,如状态、性别等。 +- **SQL 监控**: 使用 Druid 监控数据库访问性能,默认用户名和密码均为 `admin`。 +- **邮件工具**: 支持发送 HTML 格式的富文本邮件。 +- **服务监控**: 监控服务器负载情况。 -## 待办 +## 默认账号与密码 -#### 客户端 +admin / 123456 -- 20241205 启动后,主动拉取远程配置 -- 20241205 定时刷盘(刷到一个文件中) -- 20241206 同步锁加载配置 -- 20241206 定时刷盘(刷到多个文件中) -- 20241206 读取本地配置缓存(连接服务端失败后的兜底操作) -- 20241207 动态更新@Value注解的属性 -- 20241207 动态更新@ConfigurationProperties注解类中的属性 - - 感想:太艰难了 - - 感谢:spring-cloud-context 给我的灵感 - - 耗时:4小时 -- 202412xx 动态替换配置指令实现 [loading] +## 待办事项 -#### 服务端 +### 客户端 -- 客户端注册、注销 [ok] -- 多客户端支持 [ok] -- 发布配置 [loading] -- 获取客户端节点列表 [ok] +- **2024-12-05**: 启动后主动拉取远程配置 +- **2024-12-05**: 定时刷盘(刷到一个文件中) +- **2024-12-06**: 同步锁加载配置 +- **2024-12-06**: 定时刷盘(刷到多个文件中) +- **2024-12-06**: 读取本地配置缓存(连接服务端失败后的兜底操作) +- **2024-12-07**: 动态更新 `@Value` 注解的属性 + - 感想:太艰难了 + - 感谢:感谢 `spring-cloud-context` 的灵感 + - 耗时:4 小时 +- **2024-12-07**: 动态更新 `@ConfigurationProperties` 注解类中的属性 +- **2024-12-xx**: 动态替换配置指令实现 [loading] -#### Web页面 +### 服务端 -> 这个apollo页面该有的选项,我们统统都得有。当然,咱们代码纯原创哈~ 毕竟写那么烂。 - -- 应用中心: - - 所有应用 [loading] - - 我的应用 [loading] - - 收藏的应用 [loading] - - 应用详情: - - 自定义环境 [loading] - - 变更历史 [loading] - - 发布历史 [loading] - - 回滚 [loading] - - 实例列表 [loading] - - 应用授权(用户可以访问哪些环境的配置) [loading] +- **客户端注册、注销** [已完成] +- **多客户端支持** [已完成] +- **发布配置** [进行中] +- **获取客户端节点列表** [已完成] ## 更新记录 -- 20241205 成功获取远程配置,并启动子应用 - ![20241205](/doc/d20241205223929.png) +- **2024-12-05**: 成功获取远程配置并启动子应用 + ![2024-12-05](/doc/d20241205223929.png) +- **2024-12-10**: Web 页面大成 + ![2024-12-10](/doc/d20241210215050.png) -## 常见问题 +## 贡献指南 -#### win10端口被占用解决 +欢迎贡献代码!请遵循以下步骤: -```shell -# 记下最后一列pid,打开任务管理器,知道对应的进程,干掉 -netstat -ano|findstr 28010 -``` +1. **Fork 仓库** +2. **创建新分支**: `git checkout -b feature/your-feature` +3. **提交更改**: `git commit -m 'Add some feature'` +4. **推送分支**: `git push origin feature/your-feature` +5. **发起 Pull Request** + +## 许可证 + +本项目采用 [MIT License](LICENSE) 许可证。 + +## 联系我们 + +如果有任何问题或建议,请通过 [GitHub Issues](https://github.com/odboy-tianjun/kenaito-config/issues) 联系我。 \ No newline at end of file diff --git a/doc/d20241210215050.png b/doc/d20241210215050.png new file mode 100644 index 0000000000000000000000000000000000000000..99143c924ed2e5555bd098a245ee951aba91c2c6 GIT binary patch literal 166628 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 z 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�ECsgq< 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 - * 可提交一个 提交回调任务。
- * 在SpringContextHolder 初始化后,进行回调使用 - */ - -public interface CallBack { - /** - * 回调执行方法 - */ - void executor(); - - /** - * 本回调任务名称 - * - * @return / - */ - default String getCallBackName() { - return Thread.currentThread().getId() + ":" + this.getClass().getName(); - } -} - diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/context/SpringContextHolder.java b/kenaito-config-common/src/main/java/cn/odboy/config/context/SpringContextHolder.java deleted file mode 100644 index 1379562..0000000 --- a/kenaito-config-common/src/main/java/cn/odboy/config/context/SpringContextHolder.java +++ /dev/null @@ -1,140 +0,0 @@ -package cn.odboy.config.context; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@Slf4j -public class SpringContextHolder implements ApplicationContextAware, DisposableBean { - - private static ApplicationContext applicationContext = null; - private static final List CALL_BACKS = new ArrayList<>(); - private static boolean addCallback = true; - - /** - * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。 - * 在SpringContextHolder 初始化后,进行回调使用 - * - * @param callBack 回调函数 - */ - public synchronized static void addCallBacks(CallBack callBack) { - if (addCallback) { - SpringContextHolder.CALL_BACKS.add(callBack); - } else { - log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName()); - callBack.executor(); - } - } - - /** - * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. - */ - @SuppressWarnings("unchecked") - public static T getBean(String name) { - assertContextInjected(); - return (T) applicationContext.getBean(name); - } - - /** - * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. - */ - public static T getBean(Class requiredType) { - assertContextInjected(); - return applicationContext.getBean(requiredType); - } - - /** - * 获取SpringBoot 配置信息 - * - * @param property 属性key - * @param defaultValue 默认值 - * @param requiredType 返回类型 - * @return / - */ - public static T getProperties(String property, T defaultValue, Class requiredType) { - T result = defaultValue; - try { - result = getBean(Environment.class).getProperty(property, requiredType); - } catch (Exception ignored) { - } - return result; - } - - /** - * 获取SpringBoot 配置信息 - * - * @param property 属性key - * @return / - */ - public static String getProperties(String property) { - return getProperties(property, null, String.class); - } - - /** - * 获取SpringBoot 配置信息 - * - * @param property 属性key - * @param requiredType 返回类型 - * @return / - */ - public static T getProperties(String property, Class requiredType) { - return getProperties(property, null, requiredType); - } - - /** - * 检查ApplicationContext不为空. - */ - private static void assertContextInjected() { - if (applicationContext == null) { - throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" + - ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); - } - } - - /** - * 清除SpringContextHolder中的ApplicationContext为Null. - */ - private static void clearHolder() { - log.debug("清除SpringContextHolder中的ApplicationContext:" - + applicationContext); - applicationContext = null; - } - - @Override - public void destroy() { - SpringContextHolder.clearHolder(); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - if (SpringContextHolder.applicationContext != null) { - log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); - } - SpringContextHolder.applicationContext = applicationContext; - if (addCallback) { - for (CallBack callBack : SpringContextHolder.CALL_BACKS) { - callBack.executor(); - } - CALL_BACKS.clear(); - } - SpringContextHolder.addCallback = false; - } - - /** - * 获取 @Service 的所有 bean 名称 - * - * @return / - */ - public static List getAllServiceBeanName() { - return new ArrayList<>(Arrays.asList(applicationContext - .getBeanNamesForAnnotation(Service.class))); - } -} diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/model/SmallMessage.java b/kenaito-config-common/src/main/java/cn/odboy/config/model/SmallMessage.java index c69176f..f31944a 100644 --- a/kenaito-config-common/src/main/java/cn/odboy/config/model/SmallMessage.java +++ b/kenaito-config-common/src/main/java/cn/odboy/config/model/SmallMessage.java @@ -1,54 +1,71 @@ package cn.odboy.config.model; import cn.odboy.config.constant.TransferMessageType; +import java.io.Serializable; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import java.io.Serializable; - @Data @AllArgsConstructor @NoArgsConstructor public class SmallMessage implements Serializable { + /** 消息类型:cn.odboy.config.constant.TransferMessageType */ + private TransferMessageType type; + + private Response resp; + + @Data + public static class Response implements Serializable { + private Boolean success = true; + private String errorCode = "0"; + private String errorMessage = "success"; + private Object data; + /** - * 消息类型:cn.odboy.config.constant.TransferMessageType + * 创建一个表示错误响应的对象 该方法用于当请求出现问题时返回信息给客户端 + * + * @param errorMessage 错误信息,用于向客户端描述错误情况 + * @return 返回一个包含错误信息的Response对象 */ - private TransferMessageType type; - private Response resp; - - @Data - public static class Response implements Serializable { - private Boolean success = true; - private String errorCode = "0"; - private String errorMessage = "success"; - private Object data; - - public static Response bad(String errorMessage) { - Response response = new Response(); - response.setSuccess(false); - response.setErrorCode("400"); - response.setErrorMessage(errorMessage); - response.setData(null); - return response; - } - - public static Response ok(Object data, String errorMessage) { - Response response = new Response(); - response.setSuccess(true); - response.setErrorCode("0"); - response.setErrorMessage(errorMessage); - response.setData(data); - return response; - } - - public static Response ok(Object data) { - Response response = new Response(); - response.setSuccess(true); - response.setErrorCode("0"); - response.setErrorMessage("success"); - response.setData(data); - return response; - } + public static Response bad(String errorMessage) { + Response response = new Response(); + response.setSuccess(false); + response.setErrorCode("400"); + response.setErrorMessage(errorMessage); + response.setData(null); + return response; } + + /** + * 创建一个表示成功响应的对象,包含数据和错误信息 该方法用于当请求成功时返回信息和数据给客户端 + * + * @param data 成功响应的数据 + * @param errorMessage 即使在成功的情况下,也可能需要提供一些错误信息 + * @return 返回一个包含成功状态、数据和错误信息的Response对象 + */ + public static Response ok(Object data, String errorMessage) { + Response response = new Response(); + response.setSuccess(true); + response.setErrorCode("0"); + response.setErrorMessage(errorMessage); + response.setData(data); + return response; + } + + /** + * 创建一个表示成功响应的对象,仅包含数据 该方法用于当请求成功且不需要提供错误信息时使用 + * + * @param data 成功响应的数据 + * @return 返回一个包含成功状态和数据的Response对象 + */ + public static Response ok(Object data) { + Response response = new Response(); + response.setSuccess(true); + response.setErrorCode("0"); + response.setErrorMessage("success"); + response.setData(data); + return response; + } + } } diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ClientInfo.java b/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ClientInfo.java index bd19da2..13df834 100644 --- a/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ClientInfo.java +++ b/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ClientInfo.java @@ -12,9 +12,34 @@ import java.io.Serializable; */ @Data public class ClientInfo implements Serializable { + /** + * 服务器地址 + * 用于指定服务的主机名或IP地址 + */ private String server; + + /** + * 端口号 + * 用于指定服务的通信端口 + */ private Integer port; + + /** + * 环境标识 + * 用于标识当前配置所属的运行环境,例如开发、测试或生产环境 + */ private String env; + + /** + * 数据ID + * 用于唯一标识配置数据,在分布式系统中起到关键作用 + */ private String dataId; + + /** + * 缓存目录 + * 用于存储缓存数据的目录路径 + */ private String cacheDir; + } diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ConfigFileInfo.java b/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ConfigFileInfo.java index 7d3efcc..9700f18 100644 --- a/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ConfigFileInfo.java +++ b/kenaito-config-common/src/main/java/cn/odboy/config/model/msgtype/ConfigFileInfo.java @@ -10,6 +10,14 @@ import java.io.Serializable; */ @Data public class ConfigFileInfo implements Serializable { + /** + * 文件名变量,用于存储文件的名称 + */ private String fileName; + + /** + * 文件内容变量,用于存储文件的文本内容 + */ private String fileContent; + } diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/util/ChannelUtil.java b/kenaito-config-common/src/main/java/cn/odboy/config/util/ChannelUtil.java index e0263f2..4f204a8 100644 --- a/kenaito-config-common/src/main/java/cn/odboy/config/util/ChannelUtil.java +++ b/kenaito-config-common/src/main/java/cn/odboy/config/util/ChannelUtil.java @@ -2,17 +2,31 @@ package cn.odboy.config.util; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelId; + /** * 统一操作Channel + * * @author odboy * @date 2024-12-06 */ public class ChannelUtil { - public static String getId(ChannelHandlerContext ctx){ - return ctx.channel().id().asShortText(); - } + /** + * 根据ChannelHandlerContext获取通道的唯一标识符 该方法用于在处理通道相关的操作时,能够快速获取到通道的唯一标识符,以便进行后续的处理 + * + * @param ctx 通道的上下文对象,包含了通道的所有相关信息和操作方法 + * @return 返回通道的唯一标识符,以短文本形式呈现 + */ + public static String getId(ChannelHandlerContext ctx) { + return ctx.channel().id().asShortText(); + } - public static String getId(ChannelId channelId){ - return channelId.asShortText(); - } + /** + * 根据ChannelId获取通道的唯一标识符 当只有通道的标识符时,可以使用该方法获取通道的唯一标识符的短文本形式 + * + * @param channelId 通道的标识符对象,唯一标识了一个通道 + * @return 返回通道的唯一标识符,以短文本形式呈现 + */ + public static String getId(ChannelId channelId) { + return channelId.asShortText(); + } } diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/util/MessageUtil.java b/kenaito-config-common/src/main/java/cn/odboy/config/util/MessageUtil.java index 1c8158b..6492d00 100644 --- a/kenaito-config-common/src/main/java/cn/odboy/config/util/MessageUtil.java +++ b/kenaito-config-common/src/main/java/cn/odboy/config/util/MessageUtil.java @@ -15,10 +15,22 @@ import java.util.List; * @date 2024-12-06 */ public class MessageUtil { + /** + * 将给定的对象序列化为ByteBuf实例 该方法使用Protostuff库对对象进行序列化,便于在网络传输或存储 + * + * @param data 待序列化的对象 + * @return 序列化后的ByteBuf实例 + */ public static ByteBuf toByteBuf(Object data) { return Unpooled.copiedBuffer(ProtostuffUtil.serializer(data)); } + /** + * 反序列化ByteBuf为指定的SmallMessage对象 该方法主要用于处理接收到的字节数据,将其还原为对象形式 + * + * @param msg 待反序列化的ByteBuf对象,被视为字节数据源 + * @return 反序列化后的SmallMessage对象 + */ public static SmallMessage getMessage(Object msg) { ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()]; @@ -26,27 +38,56 @@ public class MessageUtil { return ProtostuffUtil.deserializer(bytes, SmallMessage.class); } - // ================ 以下为很糙的自定义方法 + /** + * 创建一个表示注册失败的ByteBuf消息 该方法用于生成一个包含错误信息的注册响应消息,便于在网络中传输 + * + * @param errorMessage 注册失败的错误信息 + * @return 包含注册失败信息的ByteBuf消息 + */ public static ByteBuf toRegisterBad(String errorMessage) { return toByteBuf( new SmallMessage(TransferMessageType.REGISTER, SmallMessage.Response.bad(errorMessage))); } + /** + * 创建一个表示注册成功的ByteBuf消息 该方法用于生成一个包含成功信息的注册响应消息,便于在网络中传输 + * + * @param data 注册成功时附带的数据 + * @return 包含注册成功信息的ByteBuf消息 + */ public static ByteBuf toRegisterOk(Object data) { return toByteBuf( new SmallMessage(TransferMessageType.REGISTER, SmallMessage.Response.ok(data))); } + /** + * 创建一个表示推送配置失败的ByteBuf消息 该方法用于生成一个包含错误信息的推送配置响应消息,便于在网络中传输 + * + * @param errorMessage 推送配置失败的错误信息 + * @return 包含推送配置失败信息的ByteBuf消息 + */ public static ByteBuf toPushConfigBad(String errorMessage) { return toByteBuf( new SmallMessage(TransferMessageType.PUSH_CONFIG, SmallMessage.Response.bad(errorMessage))); } + /** + * 创建一个表示推送配置成功的ByteBuf消息 该方法用于生成一个包含成功信息的推送配置响应消息,便于在网络中传输 + * + * @param data 推送配置成功时附带的数据 + * @return 包含推送配置成功信息的ByteBuf消息 + */ public static ByteBuf toPushConfigOk(Object data) { return toByteBuf( new SmallMessage(TransferMessageType.PUSH_CONFIG, SmallMessage.Response.ok(data))); } + /** + * 将给定的对象转换为ConfigFileInfo对象列表 该方法主要用于处理接收到的消息,将其转换为配置文件信息列表 如果给定对象不是预期的列表类型或列表为空,则返回一个新的空列表 + * + * @param o 待转换的对象 + * @return 转换后的ConfigFileInfo对象列表,如果转换失败则返回空列表 + */ public static List toConfigFileInfoList(Object o) { if (o instanceof List) { List list = (List) o; diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/util/PropertiesUtil.java b/kenaito-config-common/src/main/java/cn/odboy/config/util/PropertiesUtil.java new file mode 100644 index 0000000..f75bc48 --- /dev/null +++ b/kenaito-config-common/src/main/java/cn/odboy/config/util/PropertiesUtil.java @@ -0,0 +1,264 @@ +package cn.odboy.config.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.CollectionUtils; +import org.yaml.snakeyaml.Yaml; + +/** + * 工具类出处,https://blog.csdn.net/qq_27574367/article/details/134684434
+ * fix: 函数flattenMap中value为null导致的异常中断
+ * + * @author Deng.Weiping + * @since 2023/11/28 13:57 + */ +@Slf4j +public class PropertiesUtil { + private static final Pattern PATTERN = Pattern.compile("\\s*([^=\\s]*)\\s*=\\s*(.*)\\s*"); + private static final String PATH_SEP = "."; + + /** + * 将 YAML 字符串转换为 Properties 字符串 + * + * @param input YAML 字符串 + * @return Properties 字符串 + */ + public static String castToProperties(String input) { + Map propertiesMap = new LinkedHashMap<>(); + Map yamlMap = new Yaml().load(input); + flattenMap("", yamlMap, propertiesMap); + return propertiesMap.entrySet().stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining(StrUtil.LF)); + } + + /** + * 将 Properties 字符串转换为 YAML 字符串 + * + * @param input Properties 字符串 + * @return YAML 字符串 + */ + public static String castToYaml(String input) { + try { + Map properties = readProperties(input); + return properties2Yaml(properties); + } catch (Exception e) { + log.error("property 转 Yaml 转换异常", e); + } + return null; + } + + /** + * 将 InputStream 中的 Properties 转换为 YAML 字符串 + * + * @param inputStream 输入流 + * @return YAML 字符串 + */ + public static String castToYaml(InputStream inputStream) { + try { + Map properties = + readProperties(StrUtil.str(inputStream.readAllBytes(), StandardCharsets.UTF_8)); + return properties2Yaml(properties); + } catch (Exception e) { + log.error("property 转 Yaml 转换异常", e); + } + return null; + } + + /** + * 将字节数组中的 Properties 转换为 YAML 字符串 + * + * @param bytes 字节数组 + * @return YAML 字符串 + */ + public static String castToYaml(byte[] bytes) { + try { + Map properties = readProperties(StrUtil.str(bytes, StandardCharsets.UTF_8)); + return properties2Yaml(properties); + } catch (Exception e) { + log.error("property 转 Yaml 转换异常", e); + } + return null; + } + + /** + * 读取 Properties 字符串并转换为 Map + * + * @param input Properties 字符串 + * @return Map 对象 + */ + private static Map readProperties(String input) { + Map propertiesMap = new LinkedHashMap<>(); + for (String line : input.split(StrUtil.LF)) { + if (StrUtil.isNotBlank(line)) { + Matcher matcher = PATTERN.matcher(line); + if (matcher.matches()) { + String key = matcher.group(1); + String value = matcher.group(2); + propertiesMap.put(key, value); + } + } + } + return propertiesMap; + } + + /** + * 递归地将 Map 转换为 Properties 格式的 Map + * + * @param prefix 前缀 + * @param yamlMap YAML 格式的 Map + * @param treeMap 目标 Properties 格式的 Map + */ + private static void flattenMap( + String prefix, Map yamlMap, Map treeMap) { + yamlMap.forEach( + (key, value) -> { + if (value != null) { + String fullKey = prefix + key; + if (value instanceof LinkedHashMap) { + flattenMap(fullKey + ".", (LinkedHashMap) value, treeMap); + } else if (value instanceof ArrayList) { + List values = (List) value; + for (int i = 0; i < values.size(); i++) { + String itemKey = String.format("%s[%d]", fullKey, i); + Object itemValue = values.get(i); + if (itemValue instanceof String) { + treeMap.put(itemKey, itemValue); + } else { + flattenMap(itemKey + ".", (LinkedHashMap) itemValue, treeMap); + } + } + } else { + treeMap.put(fullKey, value.toString()); + } + } + }); + } + + /** + * 将 Properties 格式的 Map 转换为 YAML 格式的字符串 + * + * @param properties Properties 格式的 Map + * @return YAML 格式的字符串 + */ + private static String properties2Yaml(Map properties) { + if (CollUtil.isEmpty(properties)) { + return null; + } + Map map = parseToMap(properties); + return map2Yaml(map).toString(); + } + + /** + * 递归地将 Properties 格式的 Map 解析为 LinkedHashMap + * + * @param propMap Properties 格式的 Map + * @return LinkedHashMap 对象 + */ + private static Map parseToMap(Map propMap) { + Map resultMap = new LinkedHashMap<>(); + if (CollectionUtils.isEmpty(propMap)) { + return resultMap; + } + propMap.forEach( + (key, value) -> { + if (key.contains(PATH_SEP)) { + String currentKey = key.substring(0, key.indexOf(".")); + if (resultMap.get(currentKey) != null) { + return; + } + Map childMap = getChildMap(propMap, currentKey); + Map map = parseToMap(childMap); + resultMap.put(currentKey, map); + } else { + resultMap.put(key, value); + } + }); + return resultMap; + } + + /** + * 获取拥有相同父级节点的子节点 + * + * @param propMap Properties 格式的 Map + * @param currentKey 当前父级节点的键 + * @return 子节点的 Map + */ + private static Map getChildMap(Map propMap, String currentKey) { + Map childMap = new LinkedHashMap<>(); + propMap.forEach( + (key, value) -> { + if (key.contains(currentKey + PATH_SEP)) { + String subKey = key.substring(key.indexOf(".") + 1); + childMap.put(subKey, value); + } + }); + return childMap; + } + + /** + * 将 Map 集合转换为 YAML 格式的字符串 + * + * @param map Map 对象 + * @return YAML 格式的字符串 + */ + public static StringBuffer map2Yaml(Map map) { + return map2Yaml(map, 0); + } + + /** + * 将 Map 集合转换为 YAML 格式的字符串 + * + * @param propMap Map 对象 + * @param deep 树的层级 + * @return YAML 格式的字符串 + */ + private static StringBuffer map2Yaml(Map propMap, int deep) { + StringBuffer yamlBuffer = new StringBuffer(); + if (CollectionUtils.isEmpty(propMap)) { + return yamlBuffer; + } + String space = getSpace(deep); + for (Map.Entry entry : propMap.entrySet()) { + Object valObj = entry.getValue(); + String key = space + entry.getKey() + ":"; + if (valObj instanceof String) { + yamlBuffer.append(key).append(" ").append(valObj).append("\n"); + } else if (valObj instanceof List) { + yamlBuffer.append(key).append("\n"); + List list = + ((List) valObj).stream().map(Object::toString).collect(Collectors.toList()); + String lSpace = getSpace(deep + 1); + for (String str : list) { + yamlBuffer.append(lSpace).append("- ").append(str).append("\n"); + } + } else if (valObj instanceof Map) { + yamlBuffer.append(key).append("\n"); + yamlBuffer.append(map2Yaml((LinkedHashMap) valObj, deep + 1)); + } else { + yamlBuffer.append(key).append(" ").append(valObj).append("\n"); + } + } + return yamlBuffer; + } + + /** + * 获取缩进空格 + * + * @param deep 树的层级 + * @return 缩进空格字符串 + */ + private static String getSpace(int deep) { + return " ".repeat(Math.max(0, deep)); + } +} diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/util/PropertyNameUtil.java b/kenaito-config-common/src/main/java/cn/odboy/config/util/PropertyNameUtil.java index c1d460e..3bf0607 100644 --- a/kenaito-config-common/src/main/java/cn/odboy/config/util/PropertyNameUtil.java +++ b/kenaito-config-common/src/main/java/cn/odboy/config/util/PropertyNameUtil.java @@ -9,7 +9,14 @@ package cn.odboy.config.util; public class PropertyNameUtil { private static final String DEFAULT_PREFIX = "kenaito"; + /** + * 根据文件名生成带有默认前缀的文件名 + * + * @param fileName 文件名,不包含路径信息 + * @return 带有默认前缀的文件名,格式为:DEFAULT_PREFIX_fileName + */ public static String get(String fileName) { return DEFAULT_PREFIX + "_" + fileName; } + } diff --git a/kenaito-config-common/src/main/java/cn/odboy/config/util/ProtostuffUtil.java b/kenaito-config-common/src/main/java/cn/odboy/config/util/ProtostuffUtil.java index 8c28553..8474eab 100644 --- a/kenaito-config-common/src/main/java/cn/odboy/config/util/ProtostuffUtil.java +++ b/kenaito-config-common/src/main/java/cn/odboy/config/util/ProtostuffUtil.java @@ -5,7 +5,6 @@ import com.dyuproject.protostuff.LinkedBuffer; import com.dyuproject.protostuff.ProtostuffIOUtil; import com.dyuproject.protostuff.Schema; import com.dyuproject.protostuff.runtime.RuntimeSchema; - import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -14,54 +13,68 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ProtostuffUtil { - private static final Map, Schema> CACHED_SCHEMA = new ConcurrentHashMap, Schema>(); + private static final Map, Schema> CACHED_SCHEMA = + new ConcurrentHashMap, Schema>(); - private static Schema getSchema(Class clazz) { - @SuppressWarnings("unchecked") - Schema schema = (Schema) CACHED_SCHEMA.get(clazz); - if (schema == null) { - schema = RuntimeSchema.getSchema(clazz); - if (schema != null) { - CACHED_SCHEMA.put(clazz, schema); - } - } - return schema; + /** + * 获取指定类的Schema Schema用于描述对象的结构,以便于序列化和反序列化 该方法首先尝试从缓存中获取Schema,如果缓存中没有,则创建一个新的Schema并添加到缓存中 + * + * @param clazz 需要获取Schema的类 + * @return 指定类的Schema + */ + private static Schema getSchema(Class clazz) { + @SuppressWarnings("unchecked") + Schema schema = (Schema) CACHED_SCHEMA.get(clazz); + if (schema == null) { + schema = RuntimeSchema.getSchema(clazz); + if (schema != null) { + CACHED_SCHEMA.put(clazz, schema); + } } + return schema; + } - /** - * 序列化 - */ - public static byte[] serializer(T obj) { - @SuppressWarnings("unchecked") - Class clazz = (Class) obj.getClass(); - LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); - try { - Schema schema = getSchema(clazz); - return ProtostuffIOUtil.toByteArray(obj, schema, buffer); - } catch (Exception e) { - throw new IllegalStateException(e.getMessage(), e); - } finally { - buffer.clear(); - } + /** + * 序列化对象 使用Protostuff库将对象序列化为字节数组 + * + * @param obj 需要序列化的对象 + * @return 序列化后的字节数组 + */ + public static byte[] serializer(T obj) { + @SuppressWarnings("unchecked") + Class clazz = (Class) obj.getClass(); + LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); + try { + Schema schema = getSchema(clazz); + return ProtostuffIOUtil.toByteArray(obj, schema, buffer); + } catch (Exception e) { + throw new IllegalStateException(e.getMessage(), e); + } finally { + buffer.clear(); } + } - /** - * 反序列化 - */ - public static T deserializer(byte[] data, Class clazz) { - try { - T obj = clazz.newInstance(); - Schema schema = getSchema(clazz); - ProtostuffIOUtil.mergeFrom(data, obj, schema); - return obj; - } catch (Exception e) { - throw new IllegalStateException(e.getMessage(), e); - } + /** + * 反序列化字节数组为对象 使用Protostuff库将字节数组反序列化为指定类的对象 + * + * @param data 序列化后的字节数组 + * @param clazz 需要反序列化的对象类 + * @return 反序列化后的对象 + */ + public static T deserializer(byte[] data, Class clazz) { + try { + T obj = clazz.newInstance(); + Schema schema = getSchema(clazz); + ProtostuffIOUtil.mergeFrom(data, obj, schema); + return obj; + } catch (Exception e) { + throw new IllegalStateException(e.getMessage(), e); } + } - public static void main(String[] args) { - byte[] userBytes = ProtostuffUtil.serializer(new ConfigFileInfo()); - ConfigFileInfo user = ProtostuffUtil.deserializer(userBytes, ConfigFileInfo.class); - System.out.println(user); - } -} \ No newline at end of file + public static void main(String[] args) { + byte[] userBytes = ProtostuffUtil.serializer(new ConfigFileInfo()); + ConfigFileInfo user = ProtostuffUtil.deserializer(userBytes, ConfigFileInfo.class); + System.out.println(user); + } +} diff --git a/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientConfigLoader.java b/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientConfigLoader.java index c7a39bc..9489d99 100644 --- a/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientConfigLoader.java +++ b/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientConfigLoader.java @@ -117,9 +117,7 @@ public class ClientConfigLoader { ClientConfigConsts.clientInfo.wait(); } catch (InterruptedException e) { Thread currentThread = Thread.currentThread(); - String currentThreadName = currentThread.getName(); currentThread.interrupt(); - logger.error("中断线程: {}", currentThreadName, e); } } // 判断配置中心服务是否处于离线状态 @@ -175,32 +173,48 @@ public class ClientConfigLoader { }; } + /** + * 初始化客户端信息 + * + * @param defaultCacheDir 默认缓存目录如果环境变量中未指定缓存目录,则使用此默认值 + * @param environment 应用程序环境变量用于从中获取配置信息 + */ private static void initClientInfo(String defaultCacheDir, ConfigurableEnvironment environment) { + // 设置服务器地址 ClientConfigConsts.clientInfo.setServer( environment.getProperty( ClientConfigConsts.DEFAULT_CONFIG_NAME_SERVER, String.class, ClientConfigConsts.DEFAULT_CONFIG_SERVER)); + // 设置端口 ClientConfigConsts.clientInfo.setPort( environment.getProperty( ClientConfigConsts.DEFAULT_CONFIG_NAME_PORT, Integer.class, ClientConfigConsts.DEFAULT_CONFIG_PORT)); + // 设置环境 ClientConfigConsts.clientInfo.setEnv( environment.getProperty( ClientConfigConsts.DEFAULT_CONFIG_NAME_ENV, String.class, ClientConfigConsts.DEFAULT_CONFIG_ENV)); + // 设置数据ID ClientConfigConsts.clientInfo.setDataId( environment.getProperty( ClientConfigConsts.DEFAULT_CONFIG_NAME_DATA_ID, String.class, ClientConfigConsts.DEFAULT_CONFIG_DATA_ID)); + // 设置缓存目录 ClientConfigConsts.clientInfo.setCacheDir( environment.getProperty( ClientConfigConsts.DEFAULT_CONFIG_NAME_CACHE_DIR, String.class, defaultCacheDir)); } + /** + * 获取默认的缓存目录路径 根据操作系统类型返回对应的缓存目录路径 + * + * @return 默认的缓存目录路径 + */ private static String getDefaultCacheDir() { String defaultCacheDir; String os = System.getProperty("os.name"); @@ -209,16 +223,25 @@ public class ClientConfigLoader { } else if (os.toLowerCase().startsWith(ClientConfigConsts.OS_TYPE_MAC)) { defaultCacheDir = ClientConfigConsts.DEFAULT_PATH_MAC; } else { + // 对于未知操作系统,默认使用Mac操作系统的缓存路径 defaultCacheDir = ClientConfigConsts.DEFAULT_PATH_MAC; } return defaultCacheDir; } + /** + * 验证缓存目录路径的合法性 确保提供的缓存路径与默认路径格式相符,防止路径配置错误 + * + * @param defaultCacheDir 默认的缓存目录路径 + * @param cacheDir 用户配置的缓存目录路径 + */ private static void validateCacheDirPath(String defaultCacheDir, String cacheDir) { + // 检查是否为Windows系统默认路径格式,且用户配置的路径是否符合该格式 if (defaultCacheDir.contains(ClientConfigConsts.DEFAULT_PATH_WIN_SEP) && !cacheDir.contains(ClientConfigConsts.DEFAULT_PATH_WIN_SEP)) { throw new RuntimeException(ClientConfigConsts.DEFAULT_CONFIG_NAME_CACHE_DIR + " 配置的路径不正确"); } + // 检查用户配置的路径是否包含Windows系统路径分隔符,且是否正确使用 if (cacheDir.contains(ClientConfigConsts.DEFAULT_PATH_WIN_SEP) && !cacheDir.contains(ClientConfigConsts.DEFAULT_PATH_SEP_WIN)) { throw new RuntimeException( @@ -228,11 +251,20 @@ public class ClientConfigLoader { } } - /** 创建缓存文件夹 */ + /** + * 创建缓存目录 如果目录不存在,将尝试创建它并检查写权限 + * + * @param cacheDir 缓存目录的路径 + * @throws RuntimeException 如果目录创建失败或没有写权限 + */ private static void createCacheDir(String cacheDir) { + // 获取缓存目录的路径对象 Path path = Paths.get(cacheDir); + // 检查缓存目录是否存在,如果不存在则尝试创建 if (!Files.exists(path)) { + // 使用FileUtil工具类创建目录 File mkdir = FileUtil.mkdir(cacheDir); + // 检查创建后的目录是否可写,如果不可写则抛出异常 if (!mkdir.canWrite()) { throw new RuntimeException("缓存文件夹创建失败, 无读写权限"); } diff --git a/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyHelper.java b/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyHelper.java new file mode 100644 index 0000000..1d85b8d --- /dev/null +++ b/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyHelper.java @@ -0,0 +1,84 @@ +package cn.odboy.config.context; + +import cn.hutool.core.util.StrUtil; +import cn.odboy.config.constant.ClientConfigConsts; +import cn.odboy.config.constant.ClientConfigVars; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 客户端配置 辅助类 + * + *

依赖 spring-cloud-context + * + * @author odboy + * @date 2024-12-07 + */ +@Component +@RequiredArgsConstructor +public class ClientPropertyHelper { + private final ConfigurableEnvironment environment; + private final ValueAnnotationProcessor valueAnnotationProcessor; + // private final ConfigDataContextRefresher configDataContextRefresher; + private final ConfigPropertyContextRefresher contextRefresher; + + /** + * 动态更新配置值 + * + * @param propertyName 属性路径名 + * @param value 属性值 + */ + public void updateValue(String propertyName, Object value) { + if (StrUtil.isNotBlank(propertyName)) { + // 设置属性值 + MutablePropertySources propertySources = environment.getPropertySources(); + if (propertySources.contains(ClientConfigConsts.PROPERTY_SOURCE_NAME)) { + // 更新属性值 + PropertySource propertySource = + propertySources.get(ClientConfigConsts.PROPERTY_SOURCE_NAME); + Map source = ((MapPropertySource) propertySource).getSource(); + source.put(propertyName, value); + } + // 单独更新@Value对应的值 + valueAnnotationProcessor.setValue(propertyName, value); + // 刷新上下文(解决 @ConfigurationProperties注解的类属性值更新 问题) + // Spring Cloud只会对被@RefreshScope和@ConfigurationProperties标注的bean进行刷新 + // 这个方法主要做了两件事:刷新配置源,也就是PropertySource,然后刷新了@ConfigurationProperties注解的类 + // configDataContextRefresher.refresh(); + contextRefresher.refreshAll(); + } + } + + /** + * 更新所有配置属性
+ * 此方法遍历缓存的配置,更新应用程序中的相应属性
+ * 它主要针对的是那些使用@Value注解注入的配置属性
+ * 当缓存的配置发生变化时,通过此方法可以确保应用中的配置是最新的 + */ + public void updateAll() { + // 获取所有可变属性源 + MutablePropertySources propertySources = environment.getPropertySources(); + // 检查是否包含特定的属性源 + if (propertySources.contains(ClientConfigConsts.PROPERTY_SOURCE_NAME)) { + // 获取属性源 + PropertySource propertySource = + propertySources.get(ClientConfigConsts.PROPERTY_SOURCE_NAME); + // 将属性源转换为Map形式,以便于更新属性 + Map source = ((MapPropertySource) propertySource).getSource(); + // 遍历缓存的配置 + for (Map.Entry kvMap : ClientConfigVars.cacheConfigs.entrySet()) { + // 更新属性值 + source.put(kvMap.getKey(), kvMap.getValue()); + // 单独更新@Value对应的值 + valueAnnotationProcessor.setValue(kvMap.getKey(), kvMap.getValue()); + } + // 刷新所有应用上下文,使更新后的配置生效 + contextRefresher.refreshAll(); + } + } +} diff --git a/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyRefresher.java b/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyRefresher.java deleted file mode 100644 index 34664a8..0000000 --- a/kenaito-config-core/src/main/java/cn/odboy/config/context/ClientPropertyRefresher.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.odboy.config.context; - -import cn.hutool.core.util.StrUtil; -import cn.odboy.config.constant.ClientConfigConsts; -import cn.odboy.config.constant.ClientConfigVars; -import lombok.RequiredArgsConstructor; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; -import org.springframework.stereotype.Component; - -import java.util.Map; - -/** - * 客户端配置 辅助类 - *

- * 依赖 spring-cloud-context - * - * @author odboy - * @date 2024-12-07 - */ -@Component -@RequiredArgsConstructor -public class ClientPropertyRefresher { - private final ConfigurableEnvironment environment; - private final ValueAnnotationProcessor valueAnnotationProcessor; - // private final ConfigDataContextRefresher configDataContextRefresher; - private final ConfigPropertyContextRefresher contextRefresher; - - /** - * 动态更新配置值 - * - * @param propertyName 属性路径名 - * @param value 属性值 - */ - public void updateValue(String propertyName, Object value) { - if (StrUtil.isNotBlank(propertyName)) { - // 设置属性值 - MutablePropertySources propertySources = environment.getPropertySources(); - if (propertySources.contains(ClientConfigConsts.PROPERTY_SOURCE_NAME)) { - // 更新属性值 - PropertySource propertySource = propertySources.get(ClientConfigConsts.PROPERTY_SOURCE_NAME); - Map source = ((MapPropertySource) propertySource).getSource(); - source.put(propertyName, value); - } - // 单独更新@Value对应的值 - valueAnnotationProcessor.setValue(propertyName, value); - // 刷新上下文(解决 @ConfigurationProperties注解的类属性值更新 问题) - // Spring Cloud只会对被@RefreshScope和@ConfigurationProperties标注的bean进行刷新 - // 这个方法主要做了两件事:刷新配置源,也就是PropertySource,然后刷新了@ConfigurationProperties注解的类 -// configDataContextRefresher.refresh(); - contextRefresher.refreshAll(); - } - } - - public void updateAll() { - // 设置属性值 - MutablePropertySources propertySources = environment.getPropertySources(); - if (propertySources.contains(ClientConfigConsts.PROPERTY_SOURCE_NAME)) { - // 更新属性值 - PropertySource propertySource = propertySources.get(ClientConfigConsts.PROPERTY_SOURCE_NAME); - Map source = ((MapPropertySource) propertySource).getSource(); - for (Map.Entry kvMap : ClientConfigVars.cacheConfigs.entrySet()) { - source.put(kvMap.getKey(), kvMap.getValue()); - // 单独更新@Value对应的值 - valueAnnotationProcessor.setValue(kvMap.getKey(), kvMap.getValue()); - } - contextRefresher.refreshAll(); - } - } -} diff --git a/kenaito-config-demo/src/main/java/cn/odboy/rest/DemoController.java b/kenaito-config-demo/src/main/java/cn/odboy/rest/DemoController.java index 0bcc453..430d2d8 100644 --- a/kenaito-config-demo/src/main/java/cn/odboy/rest/DemoController.java +++ b/kenaito-config-demo/src/main/java/cn/odboy/rest/DemoController.java @@ -1,6 +1,6 @@ package cn.odboy.rest; -import cn.odboy.config.context.ClientPropertyRefresher; +import cn.odboy.config.context.ClientPropertyHelper; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; @@ -21,7 +21,7 @@ public class DemoController { @Value("${kenaito.config-center.test}") private String testStr; private final ConfigCenterProperties configCenterProperties; - private final ClientPropertyRefresher clientPropertyRefresher; + private final ClientPropertyHelper clientPropertyHelper; /** 配置变化了 */ @GetMapping("/test") @@ -29,7 +29,7 @@ public class DemoController { String propertyName = "kenaito.config-center.test"; System.err.println("@Value注解的值1=" + testStr); System.err.println("@ConfigurationProperties注解的值1=" + configCenterProperties.getTest()); - clientPropertyRefresher.updateValue(propertyName, "Hello World"); + clientPropertyHelper.updateValue(propertyName, "Hello World"); System.err.println("@Value注解的值2=" + testStr); System.err.println("@ConfigurationProperties注解的值2=" + configCenterProperties.getTest()); return ResponseEntity.ok("success"); diff --git a/kenaito-config-service/src/main/java/cn/odboy/domain/ConfigApp.java b/kenaito-config-service/src/main/java/cn/odboy/domain/ConfigApp.java index 5f7b002..dcf346a 100644 --- a/kenaito-config-service/src/main/java/cn/odboy/domain/ConfigApp.java +++ b/kenaito-config-service/src/main/java/cn/odboy/domain/ConfigApp.java @@ -65,9 +65,9 @@ public class ConfigApp extends MyNormalEntity { @Data public static class QueryClientArgs { @NotBlank(message = "必填") - private String env; + private String envCode; @NotBlank(message = "必填") - private String dataId; + private String appName; } @Data diff --git a/kenaito-config-service/src/main/java/cn/odboy/infra/netty/ConfigClientManage.java b/kenaito-config-service/src/main/java/cn/odboy/infra/netty/ConfigClientManage.java index 381803f..d2c286d 100644 --- a/kenaito-config-service/src/main/java/cn/odboy/infra/netty/ConfigClientManage.java +++ b/kenaito-config-service/src/main/java/cn/odboy/infra/netty/ConfigClientManage.java @@ -6,13 +6,12 @@ import cn.odboy.infra.exception.BadRequestException; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelId; -import lombok.extern.slf4j.Slf4j; - import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; /** * 客户端管理 @@ -22,110 +21,108 @@ import java.util.stream.Collectors; */ @Slf4j public class ConfigClientManage { - /** - * 所有的客户端连接: {env}_{dataId}_{channelId} to ctx - */ - private static final ConcurrentMap CLIENT = new ConcurrentHashMap<>(); + /** 所有的客户端连接: {env}_{dataId}_{channelId} to ctx */ + private static final ConcurrentMap CLIENT = new ConcurrentHashMap<>(); - /** - * 查询客户端节点列表 - * - * @param env 环境编码 - * @param dataId 应用名称 - * @return / - */ - private static List queryClientInfos(String env, String dataId) { - String filterKey = String.format("%s_%s_", env, dataId); - return CLIENT.entrySet().stream() - .filter(f -> f.getKey().startsWith(filterKey)) - .map(Map.Entry::getValue) - .map( - m -> { - ConfigApp.ClientInfo clientInfo = new ConfigApp.ClientInfo(); - clientInfo.setIp(m.remoteAddress().toString().replaceAll("/", "")); - clientInfo.setIsActive(m.isActive()); - return clientInfo; - }) - .collect(Collectors.toList()); - } + /** + * 查询客户端节点列表 + * + * @param envCode 环境编码 + * @param appName 应用名称 + * @return / + */ + private static List queryClientInfos(String envCode, String appName) { + String filterKey = String.format("%s_%s_", envCode, appName); + return CLIENT.entrySet().stream() + .filter(f -> f.getKey().startsWith(filterKey)) + .map(Map.Entry::getValue) + .map( + m -> { + ConfigApp.ClientInfo clientInfo = new ConfigApp.ClientInfo(); + clientInfo.setIp(m.remoteAddress().toString().replaceAll("/", "")); + clientInfo.setIsActive(m.isActive()); + return clientInfo; + }) + .collect(Collectors.toList()); + } - /** - * 客户端注册 - * - * @param env 环境编码 - * @param dataId 应用名称 - * @param ctx 信道 - */ - public static void register(String env, String dataId, ChannelHandlerContext ctx) { - String envClientKey = String.format("%s_%s_%s", env, dataId, ChannelUtil.getId(ctx)); - CLIENT.put(envClientKey, ctx.channel()); - log.info("客户端 {} 注册成功", envClientKey); - } + /** + * 客户端注册 + * + * @param env 环境编码 + * @param dataId 应用名称 + * @param ctx 信道 + */ + public static void register(String env, String dataId, ChannelHandlerContext ctx) { + String envClientKey = String.format("%s_%s_%s", env, dataId, ChannelUtil.getId(ctx)); + CLIENT.put(envClientKey, ctx.channel()); + log.info("客户端 {} 注册成功", envClientKey); + } - /** - * 客户端注销 - * - * @param channelId / - */ - public static void unregister(ChannelId channelId) { - List envClientKeys = - CLIENT.keySet().stream() - .filter(f -> f.endsWith(ChannelUtil.getId(channelId))) - .collect(Collectors.toList()); - for (String envClientKey : envClientKeys) { - Channel channel = CLIENT.getOrDefault(envClientKey, null); - if (channel != null) { - if (channel.isOpen()) { - channel.closeFuture(); - } - CLIENT.remove(envClientKey); - log.info("客户端 {} 注销成功", envClientKey); - } + /** + * 客户端注销 + * + * @param channelId / + */ + public static void unregister(ChannelId channelId) { + List envClientKeys = + CLIENT.keySet().stream() + .filter(f -> f.endsWith(ChannelUtil.getId(channelId))) + .collect(Collectors.toList()); + for (String envClientKey : envClientKeys) { + Channel channel = CLIENT.getOrDefault(envClientKey, null); + if (channel != null) { + if (channel.isOpen()) { + channel.closeFuture(); } + CLIENT.remove(envClientKey); + log.info("客户端 {} 注销成功", envClientKey); + } } + } - /** - * 根据env和dataId查询所有客户端节点 - * - * @param env 环境编码 - * @param dataId 应用名称 - * @return / - */ - public static List queryChannels(String env, String dataId) { - String filterKey = String.format("%s_%s_", env, dataId); - return CLIENT.entrySet().stream() - .filter(f -> f.getKey().startsWith(filterKey)) - .map(Map.Entry::getValue) - .collect(Collectors.toList()); - } + /** + * 根据env和dataId查询所有客户端节点 + * + * @param env 环境编码 + * @param dataId 应用名称 + * @return / + */ + public static List queryChannels(String env, String dataId) { + String filterKey = String.format("%s_%s_", env, dataId); + return CLIENT.entrySet().stream() + .filter(f -> f.getKey().startsWith(filterKey)) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + } - /** - * 根据channelId获取env和dataId - * - * @param channelId / - * @return / - */ - public static String[] getEnvDataId(ChannelId channelId) { - String envClientKey = - CLIENT.keySet().stream() - .filter(f -> f.endsWith(ChannelUtil.getId(channelId))) - .findFirst() - .orElse(null); - if (envClientKey == null) { - throw new BadRequestException("获取配置数据ID失败"); - } - String[] s = envClientKey.split("_"); - // 最大分割块数 - int maxSplitLength = 3; - if (s.length != maxSplitLength) { - throw new BadRequestException("获取配置数据ID失败"); - } - return s; + /** + * 根据channelId获取env和dataId + * + * @param channelId / + * @return / + */ + public static String[] getEnvDataId(ChannelId channelId) { + String envClientKey = + CLIENT.keySet().stream() + .filter(f -> f.endsWith(ChannelUtil.getId(channelId))) + .findFirst() + .orElse(null); + if (envClientKey == null) { + throw new BadRequestException("获取配置数据ID失败"); } + String[] s = envClientKey.split("_"); + // 最大分割块数 + int maxSplitLength = 3; + if (s.length != maxSplitLength) { + throw new BadRequestException("获取配置数据ID失败"); + } + return s; + } - public static Object queryClientInfos(ConfigApp.QueryClientArgs args) { - String dataId = args.getDataId(); - String env = args.getEnv(); - return queryClientInfos(env, dataId); - } + public static Object queryClientInfos(ConfigApp.QueryClientArgs args) { + String appName = args.getAppName(); + String envCode = args.getEnvCode(); + return queryClientInfos(envCode, appName); + } } diff --git a/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigAppEnvServiceImpl.java b/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigAppEnvServiceImpl.java index 2a196dc..c9391fc 100644 --- a/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigAppEnvServiceImpl.java +++ b/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigAppEnvServiceImpl.java @@ -71,11 +71,7 @@ public class ConfigAppEnvServiceImpl extends ServiceImpl configFileId = configFiles.stream().map(ConfigFile::getId).collect(Collectors.toList()); configFileService.removeBatchByIds(configFileId); - Long fileId = configFileId.stream().findFirst().orElse(null); - if (fileId != null) { - // delete from config_version - configVersionService.removeBatchByFileId(fileId); - } + configFileId.stream().findFirst().ifPresent(configVersionService::removeBatchByFileId); } } } diff --git a/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigFileServiceImpl.java b/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigFileServiceImpl.java index ebcfff1..89ad619 100644 --- a/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigFileServiceImpl.java +++ b/kenaito-config-service/src/main/java/cn/odboy/service/impl/ConfigFileServiceImpl.java @@ -1,8 +1,11 @@ package cn.odboy.service.impl; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.stream.StreamUtil; import cn.hutool.core.util.StrUtil; import cn.odboy.config.model.msgtype.ConfigFileInfo; +import cn.odboy.config.util.PropertiesUtil; import cn.odboy.domain.ConfigFile; import cn.odboy.domain.ConfigVersion; import cn.odboy.infra.exception.BadRequestException; @@ -14,10 +17,14 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import org.yaml.snakeyaml.Yaml; /** * 配置文件 服务实现类 @@ -117,6 +124,14 @@ public class ConfigFileServiceImpl extends ServiceImpl