commit f285fffe30aae75aaa0bba85f674bc2248ce2649 Author: eson <474420502@qq.com> Date: Sun Jun 19 05:56:05 2022 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe0df52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,94 @@ +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,java,maven +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,linux,java,maven + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# Eclipse m2e generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +.vscode/*.code-snippets + +# Ignore code-workspaces +*.code-workspace + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,java,maven + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..69aff39 --- /dev/null +++ b/pom.xml @@ -0,0 +1,92 @@ + + 4.0.0 + com.yuandian.common + config + 1.0.0-SNAPSHOT + jar + config + http://yuandian.com + + UTF-8 + 17 + 17 + 17 + 2.1.0 + 1.30 + 1.7.36 + + + + + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + + com.alibaba.nacos + nacos-client + ${nacos.version} + + + + org.projectlombok + lombok + 1.18.24 + provided + + + + + + yuandian-nexus + yuandian-snapshots + http://mvn.yuandian.com/repository/maven-snapshots + + + yuandian-nexus + yuandian-public + http://mvn.yuandian.com/repository/maven-public + + + + + + org.sonatype.plugins + nxrm3-maven-plugin + 1.0.4 + + + default-deploy + deploy + + deploy + + + + + http://mvn.yuandian.com + + yuandian-nexus + + maven-snapshots + + true + + + + + \ No newline at end of file diff --git a/src/main/java/com/yuandian/common/Config.java b/src/main/java/com/yuandian/common/Config.java new file mode 100644 index 0000000..63a3369 --- /dev/null +++ b/src/main/java/com/yuandian/common/Config.java @@ -0,0 +1,263 @@ +/** + * description + * + * @author eson + *2022年6月15日-17:38:16 + */ +package com.yuandian.common; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; +import org.yaml.snakeyaml.Yaml; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import com.alibaba.nacos.api.exception.NacosException; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +/** + * description nacos配置. + * + * @author eson + *2022年6月13日-17:08:46 + */ +@Slf4j +@Getter +@Setter +public class Config { + public static String DEFAULT_CONFIT_FILE = "application.properties"; + public static String DEFAULT_CONFIG_ADDR = "yuandian.dataflow.config.nacos.server.addr"; + // public static String DEFAULT_CONFIG_DATAID = "yuandian.dataflow.config.nacos.dataid"; + // public static String DEFAULT_CONFIG_GROUP = "yuandian.dataflow.config.nacos.group";+ + // 默认 + public static String DEFAULT_GROUP_DATAID = "yuandian.dataflow"; + // 所有生成的nacos客户端 + private static HashMap configDict = new HashMap<>(); + + // 配置的所有值主类 + public Map data; + // nacos地址 + public String serverAddr ; + // nacos dataId + public String dataId ; + // nacos group + public String group ; + // 线程安全配置锁 + private Lock datalock; + // nacos 客户端类 + private ConfigService configService; + private Config(String GroupAndDataId) throws Exception { + String[] gad = GroupAndDataId.split("\\."); + if(gad.length != 2) { + throw new Exception("Group 或者 DataId 不能存在 '.' 的命令"); + } + this.group = gad[0] + ENV_TEST; + this.dataId = gad[1] + ENV_TEST; + connect(); + } + /** + * 连接nacos + * @throws IOException + * @throws NacosException + */ + private void connect() throws IOException, NacosException { + if(configService != null) { + configService.shutDown(); + configService = null; + } + // 获取 app + Properties prop = new Properties(); + prop.load(Config.class.getClassLoader().getResourceAsStream(DEFAULT_CONFIT_FILE)); + + serverAddr = prop.getProperty(DEFAULT_CONFIG_ADDR + ENV_TEST).trim(); + // dataId = prop.getProperty(DEFAULT_CONFIG_DATAID).trim(); + // group = prop.getProperty(DEFAULT_CONFIG_GROUP).trim(); + + Properties properties = new Properties(); + properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); + configService = NacosFactory.createConfigService(properties); + String content = configService.getConfig(dataId, group, 5000); + Yaml yaml = new Yaml(); + data = yaml.load(content); + log.info(content); + + datalock = new ReentrantLock(); + // 监听 配置更新事件 + configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + log.debug("recieve:" + configInfo); + try { + datalock.lock(); + data = (Map)new Yaml().load(configInfo); + log.debug("{}",data); + } finally { + datalock.unlock(); + } + } + + @Override + public Executor getExecutor() { + return null; + } + }); + } + /** + * 根据多个key获取yaml的值 keys 路径 + * @param keys 获取的key值 + * @return + */ + public Object get(String ...keys) { + var cur = data; + for(var i = 0; i < keys.length - 1;i++ ) { + var key = keys[i]; + cur = (Map) cur.get(key); + } + return cur.get(keys[keys.length - 1]); + } + /** + * + * 用于定位keys的路径后的操作. 创建keys后赋值. 如果存在keys, 可以直接赋值, 不存在则直接报错 + * + * @author eson + *2022年6月15日-下午12:06:37 + */ + public class Operator { + Config config; + String[] keys; + Operator(Config config, String[] keys) { + this.config = config; + this.keys = keys; + } + /** + * 创建seek的key + * @return + */ + public Operator createKeys() { + var cur = config.data; + for(var i = 0; i < keys.length;i++ ) { + var key = keys[i]; + var vobj = cur.get(key); + if (vobj == null) { + vobj = new LinkedHashMap<>(); + cur.put(key, vobj); + } + cur = (Map) vobj; + } + return this; + } + /** + * 定位后赋值 + * @param value + */ + public void set(Object value) { + var cur = config.data; + for(var i = 0; i < keys.length - 1;i++ ) { + var key = keys[i]; + cur = (Map) cur.get(key); + } + cur.put(keys[keys.length - 1], value); + } + } + /** + * 定位. eg. seek("key1", "key2") + * @param keys + * @return + */ + public Operator seek(String ...keys) { + return new Operator(this, keys); + } + /** + * 删除 key的值. 类型map操作. keys是一个路径 remove("a","b") --> {"a": {"b": 1}} 删除b + * @param keys + * @return + */ + public Object remove(String ...keys) { + var cur = data; + for(var i = 0; i < keys.length - 1;i++ ) { + var key = keys[i]; + cur = (Map) cur.get(key); + } + return cur.remove(keys[keys.length - 1]); + } + /** + * 更新配置 + * @return 返回是否发布成功. + * @throws NacosException + */ + public Boolean update() throws NacosException { + return configService.publishConfig(dataId, group,new Yaml().dumpAsMap(data)); + } + public static String ENV_TEST = ""; + /** + * 统一使用配置的入口函数 线程安全 + * @param GroupAndDataID 使用配置的标签 eg."group.dataId" + * @param execute 匿名函数 + * @return 函数返回的值, 如果不需要直接返回null + * @throws Exception + */ + public static Object UseConfig(String GroupAndDataID, Function execute) throws Exception { + log.info(GroupAndDataID); + Config cnf; + synchronized(configDict) { + cnf = configDict.get(GroupAndDataID); + if(cnf == null) { + cnf = new Config(GroupAndDataID); + configDict.put(GroupAndDataID, cnf); + } + } + try { + cnf.datalock.lock(); + var res = execute.apply(cnf); + return res; + } catch (Exception e) { + throw e; + } finally { + cnf.datalock.unlock(); + } + } + /** + * 统一使用配置的入口函数 线程安全 + * @param execute 统一使用配置的入口匿名方法 + * @return 函数返回的值, 如果不需要直接返回null + * @throws Exception + */ + public static Object UseConfig(Function execute) throws Exception { + return UseConfig(DEFAULT_GROUP_DATAID, execute); + } + private static String Trim(String o, String mychars){ + if(o == null) { + return null; + } else if (o.length() == 0) { + return o; + } + var buf = o.toCharArray(); + var chars = mychars.toCharArray(); + var start = 0; + for( ; start < buf.length; start++) { + for(var c: chars){ + if(buf[start] != c) { + break; + } + } + } + var end = buf.length; + for( ; end >= 0; end--) { + for(var c: chars){ + if(buf[start] != c) { + break; + } + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..3f68077 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,6 @@ +server.port=3440 + +yuandian.dataflow.config.nacos.server.addr="192.168.1.113:8848" +yuandian.dataflow.config.nacos.server.addr-test="192.168.1.113:8848" + + \ No newline at end of file diff --git a/src/test/java/com/yuandian/common/ConfigTest.java b/src/test/java/com/yuandian/common/ConfigTest.java new file mode 100644 index 0000000..754ab5f --- /dev/null +++ b/src/test/java/com/yuandian/common/ConfigTest.java @@ -0,0 +1,99 @@ +package com.yuandian.common; + +import java.time.Instant; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import com.alibaba.nacos.api.exception.NacosException; +import com.yuandian.common.Config; + +import lombok.extern.slf4j.Slf4j; +@Slf4j +@TestMethodOrder(OrderAnnotation.class) +public class ConfigTest { + /** + * 测试配置基础用法 + * @throws Exception + */ + @Test + @Order(1) + void testUseConfig() throws Exception { + Config.ENV_TEST = "-test"; + Config.UseConfig((cnf) -> { + log.info("{}",cnf.data); + + Assertions.assertEquals(cnf.get("key1", "key2"), "key_path"); + Instant now = Instant.now(); + cnf.data.put("use_config", now.toString()); + try { + log.info("{}",cnf.update()); + } catch (NacosException e) { + e.printStackTrace(); + } + return null; + }); + } + @Test + @Order(2) + void testRemove() throws Exception { + Config.ENV_TEST = "-test"; + Config.UseConfig((cnf) -> { + cnf.remove("create1", "create2"); + try { + log.info("{}",cnf.update()); // 所有增加删除操作要最后同步到nacos. 都需要update + } catch (NacosException e) { + e.printStackTrace(); + } + cnf.remove("create1"); + try { + log.info("{}",cnf.update()); // 所有增加删除操作要最后同步到nacos. 都需要update + } catch (NacosException e) { + e.printStackTrace(); + } + return null; + }); + } + @Test + @Order(3) + void testCreateKeys() throws Exception { + Config.ENV_TEST = "-test"; + + Config.UseConfig((cnf) -> { + log.info("{}",cnf.data); + cnf.seek("create1", "create2", "create3").createKeys().set(Instant.now().toString());; + try { + log.info("{}",cnf.update()); // 所有增加删除操作要最后同步到nacos. 都需要update + } catch (NacosException e) { + e.printStackTrace(); + } + return null; + }); + } + + @Test + @Order(1) + void testUpdate() throws Exception { + Config.ENV_TEST = "-test"; + Config.UseConfig((cnf) -> { + Instant now = Instant.now(); + cnf.data.put("use_config", now.toString()); + try { + log.info("{}",cnf.update()); + } catch (NacosException e) { + e.printStackTrace(); + } + return null; + }); + } + @Test + @Order(1) + void testLabelConfig() throws Exception { + Config.UseConfig("org.fortest", (cnf)->{ + log.info("{}", cnf.get("test")); + Assertions.assertEquals(cnf.get("test"), "groupAndDataId"); + return null; + }); + } +} \ No newline at end of file