mirror of
https://gitee.com/nutz/nutzboot.git
synced 2024-11-30 02:38:28 +08:00
Merge pull request #110 from nutzam/add_nutzboot-starter-config-client
add: starter-config-client NB配置中心客户端
This commit is contained in:
commit
491554aeae
@ -5,6 +5,7 @@
|
||||
* 变更:
|
||||
* add: starter-tio by [蛋蛋](https://github.com/TopCoderMyDream)
|
||||
* add: starter-apollo-client 对接apollo配置中心. apollo是携程框架部门研发的分布式配置中心
|
||||
* add: starter-config-client NB配置中心的客户端,其服务端可以是任意支持Restful的服务器
|
||||
* add: feign支持从ioc容器获取client/encoder/decoder,且自定义JsonFormat
|
||||
* add: starter-eureka-server新版的status页面 by [温泉](https://github.com/ywjno)
|
||||
* update: 更新到HikariCP 2.7.5
|
||||
|
@ -144,8 +144,9 @@ public class MainLauncher {
|
||||
- [x] starter-[eureka-server](https://github.com/Netflix/eureka) 服务治理的服务器端
|
||||
- [x] starter-[eureka-client](https://github.com/Netflix/eureka) 服务治理的客户端
|
||||
- 配置中心
|
||||
- [ ] NB Config Server/Client
|
||||
- [x] starter-[apollo-client](https://github.com/ctripcorp/apollo) 携程框架部门研发的分布式配置中心
|
||||
- [ ] NB Config Server 配置中心的服务端
|
||||
- [x] starter-config-client NB Config Client 配置中心的客户端
|
||||
- [x] starter-[apollo-client](https://github.com/ctripcorp/apollo) 携程框架部门研发的分布式配置中心的客户端
|
||||
- API网关
|
||||
- [ ] NB API网关
|
||||
- [ ] zuul
|
||||
|
@ -46,11 +46,7 @@ public class PropertiesConfigureLoader extends AbstractConfigureLoader {
|
||||
}
|
||||
if (flag) {
|
||||
// 加载application.properties
|
||||
try (InputStream ins = resourceLoader.get(path)) {
|
||||
if (ins != null) {
|
||||
conf.load(Streams.utf8r(ins), false);
|
||||
}
|
||||
}
|
||||
readPropertiesPath(path);
|
||||
}
|
||||
// 也许命令行里面指定了profile,需要提前load进来
|
||||
PropertiesProxy tmp = new PropertiesProxy();
|
||||
@ -66,12 +62,8 @@ public class PropertiesConfigureLoader extends AbstractConfigureLoader {
|
||||
// 加载指定profile,如果有的话
|
||||
if (conf.has("nutz.profiles.active")) {
|
||||
String profile = conf.get("nutz.profiles.active");
|
||||
path = path.substring(0, path.lastIndexOf('.')) + "-" + profile + ".properties";
|
||||
try (InputStream ins = resourceLoader.get(path)) {
|
||||
if (ins != null) {
|
||||
conf.load(Streams.utf8r(ins), false);
|
||||
}
|
||||
}
|
||||
String _path = path.substring(0, path.lastIndexOf('.')) + "-" + profile + ".properties";
|
||||
readPropertiesPath(_path);
|
||||
}
|
||||
// 如果conf内含有nutz.boot.configure.properties.dir配置,则读取该目录下的所有配置文件
|
||||
// 配置示例: nutz.boot.configure.properties.dir=config, 那么读取的就是jar包当前目录下config子目录下的所有properties文件
|
||||
@ -120,4 +112,12 @@ public class PropertiesConfigureLoader extends AbstractConfigureLoader {
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
protected void readPropertiesPath(String path) throws IOException {
|
||||
try (InputStream ins = resourceLoader.get(path)) {
|
||||
if (ins != null) {
|
||||
conf.load(Streams.utf8r(ins), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
<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>org.nutz</groupId>
|
||||
<artifactId>nutzboot-demo-simple</artifactId>
|
||||
<version>2.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nutzboot-demo-simple-config-client</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter-nutz-mvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter-config-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter-jetty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/nutz/org.nutz.boot.starter.NbStarter</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>io.nutz.demo.simple.MainLauncher</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,44 @@
|
||||
package io.nutz.demo.simple;
|
||||
|
||||
import org.nutz.boot.NbApp;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
/**
|
||||
* 注意, 本demo需要配合配置中心的服务端使用, 为了演示, 使用的是 http://nbconfig.nutz.cn/ <p/>
|
||||
*
|
||||
* 服务端可以是nginx/apache等一切静态文件服务器, 也可以是nutz mvc/nutz boot app等一切能提供web服务的进程
|
||||
* @author wendal
|
||||
*
|
||||
*/
|
||||
@IocBean
|
||||
public class MainLauncher {
|
||||
/*
|
||||
本demo的一些说明:
|
||||
1. META-INF/app.properties 是可选文件, 其中的参数用于拼接URL,规则是
|
||||
|
||||
http://${config.hosts}/${config.zone}/${app.id}/application.properties
|
||||
http://${config.hosts}/${config.zone}/${app.id}/application-dev.properties
|
||||
http://${config.hosts}/${config.zone}/${app.id}/application-prod.properties
|
||||
|
||||
里面的参数也可以通过 java -Dapp.id=XXX -Dconfig.hosts=YYY myjar.jar 进行设置
|
||||
2. config.hosts可以是多个域名,用逗号分隔
|
||||
3. 安全机制是可选的,默认是http basic auth, 以 ${config.zone}作为用户名, ${config.password}作为密码
|
||||
*/
|
||||
@Inject
|
||||
protected PropertiesProxy conf;
|
||||
|
||||
@Ok("json:full")
|
||||
@At("/config/getall")
|
||||
public PropertiesProxy getAll() {
|
||||
return conf;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new NbApp().setPrintProcDoc(true).run();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
app.id=SampleApp
|
||||
config.zone=guest
|
||||
config.token=123456
|
||||
config.password=ABC123456
|
||||
config.hosts=nbconfig.nutz.cn
|
@ -0,0 +1,7 @@
|
||||
log4j.rootLogger=debug,Console
|
||||
|
||||
log4j.logger.org.eclipse.jetty=info
|
||||
|
||||
log4j.appender.Console=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.Console.layout.ConversionPattern=[%-5p] %d{HH:mm:ss.SSS} %l - %m%n
|
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello, So NB!</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello, So NB!
|
||||
</body>
|
||||
</html>
|
@ -10,7 +10,7 @@
|
||||
<artifactId>nutzboot-demo-simple-tio</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>nutzboot-demo-simple.tio</name>
|
||||
<name>nutzboot-demo-simple-tio</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
|
@ -2,15 +2,18 @@ package org.nutz.boot.starter.apollo;
|
||||
|
||||
import org.nutz.boot.AppContext;
|
||||
import org.nutz.boot.starter.ServerFace;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import com.ctrip.framework.apollo.Config;
|
||||
import com.ctrip.framework.apollo.ConfigChangeListener;
|
||||
import com.ctrip.framework.apollo.ConfigService;
|
||||
import com.ctrip.framework.apollo.model.ConfigChange;
|
||||
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
|
||||
|
||||
@IocBean
|
||||
public class ApolloConfigureChangeStarter implements ServerFace {
|
||||
public class ApolloConfigureChangeStarter implements ServerFace, ConfigChangeListener {
|
||||
|
||||
@Inject
|
||||
protected AppContext appContext;
|
||||
@ -20,4 +23,25 @@ public class ApolloConfigureChangeStarter implements ServerFace {
|
||||
appContext.getBeans(ConfigChangeListener.class).forEach((listener) -> config.addChangeListener(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(ConfigChangeEvent changeEvent) {
|
||||
PropertiesProxy conf = appContext.getConf();
|
||||
for (String key : changeEvent.changedKeys()) {
|
||||
ConfigChange cc = changeEvent.getChange(key);
|
||||
switch (cc.getChangeType()) {
|
||||
case ADDED:
|
||||
conf.put(key, cc.getNewValue());
|
||||
break;
|
||||
case MODIFIED:
|
||||
conf.put(key, cc.getNewValue());
|
||||
break;
|
||||
case DELETED:
|
||||
conf.remove(key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
16
nutzboot-starter/nutzboot-starter-config-client/pom.xml
Normal file
16
nutzboot-starter/nutzboot-starter-config-client/pom.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<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>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter</artifactId>
|
||||
<version>2.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nutzboot-starter-config-client</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>nutzboot-starter-config-client</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<description>NutzBoot, micoservice base on Nutz</description>
|
||||
</project>
|
@ -0,0 +1,127 @@
|
||||
package org.nutz.cloud.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.nutz.cloud.config.spi.ConfigureEventHandler;
|
||||
import org.nutz.http.Request;
|
||||
import org.nutz.http.Request.METHOD;
|
||||
import org.nutz.http.Response;
|
||||
import org.nutz.http.Sender;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.repo.Base64;
|
||||
|
||||
public class CloudConfig {
|
||||
|
||||
protected static Properties selfConf;
|
||||
|
||||
public static void init() {
|
||||
Properties selfConf = new Properties();
|
||||
InputStream ins = CloudConfig.class.getClassLoader().getResourceAsStream("META-INF/app.properties");
|
||||
if (ins != null) {
|
||||
try {
|
||||
selfConf.load(ins);
|
||||
selfConf.putIfAbsent("app.id", "SampleApp");
|
||||
selfConf.putIfAbsent("config.zone", "guest");
|
||||
selfConf.putIfAbsent("config.token", "123456");
|
||||
selfConf.putIfAbsent("config.label", "HEAD");
|
||||
selfConf.putIfAbsent("config.type", "simple");
|
||||
selfConf.putIfAbsent("config.hosts", "nbconfig.nutz.cn");
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("META-INF/app.properties can't read!", e);
|
||||
}
|
||||
}
|
||||
CloudConfig.selfConf = selfConf;
|
||||
|
||||
}
|
||||
|
||||
public static void fromRemote(PropertiesProxy conf, String fileName) {
|
||||
NutMap params = new NutMap();
|
||||
params.put("id", getSelfConfig("app.id", null));
|
||||
params.put("zone", getSelfConfig("app.zone", "guest"));
|
||||
params.put("token", getSelfConfig("config.token", "123456"));
|
||||
params.put("label", getSelfConfig("config.label", "HEAD"));
|
||||
params.put("password", getSelfConfig("config.password", "ABC123456"));
|
||||
String hosts = getSelfConfig("config.hosts", null);
|
||||
String type = getSelfConfig("config.type", "simple");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (String host : Strings.splitIgnoreBlank(hosts)) {
|
||||
try {
|
||||
switch (type) {
|
||||
// spring-cloud-config-server
|
||||
case "spring":
|
||||
bySpring(params, host);
|
||||
return;
|
||||
// 访问 eureka 获取服务器列表,然后按simple处理
|
||||
case "eureku":
|
||||
byEureka(params, host);
|
||||
return;
|
||||
// 简单模式, 也许就是nutzcloud-config-server的模块, 按路径取就行啦
|
||||
case "simple":
|
||||
default:
|
||||
bySimple(conf, params, host, fileName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Logs.get().infof("fail at type=%s host=%s", type, host);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("can't fetch config from remote");
|
||||
}
|
||||
|
||||
protected static void bySimple(PropertiesProxy conf, NutMap params, String host, String fileName) {
|
||||
String url = String.format("http://%s/%s/%s/%s", host, params.get("zone"), params.get("id"), fileName);
|
||||
try {
|
||||
Request req = Request.create(url, METHOD.GET);
|
||||
long now = System.currentTimeMillis();
|
||||
String once = R.UU32();
|
||||
String sign = Lang.sha1(String.format("%s,%s,%s,%s,%s", params.get("zone"), params.get("id"), now, params.get("token"), once));
|
||||
req.getHeader().set("X-Client-Once", once);
|
||||
req.getHeader().set("X-Client-Time", now + "");
|
||||
req.getHeader().set("X-Client-Sign", sign);
|
||||
req.getHeader().set("Authorization", Base64.encodeToString((params.get("zone") + ":" + params.getString("password")).getBytes(), false));
|
||||
Response resp = Sender.create(req).setTimeout(3000).send();
|
||||
if (resp.isOK()) {
|
||||
conf.load(resp.getReader());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Logs.get().warnf("url=%s code=%s", url, resp.getStatus());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logs.get().warn("url=" + url, e);
|
||||
}
|
||||
throw new RuntimeException("FAILED url=" + url);
|
||||
}
|
||||
|
||||
protected static PropertiesProxy bySpring(NutMap params, String host) {
|
||||
throw Lang.noImplement();
|
||||
}
|
||||
|
||||
protected static PropertiesProxy byEureka(NutMap params, String host) {
|
||||
throw Lang.noImplement();
|
||||
}
|
||||
|
||||
public static void addListener(ConfigureEventHandler listener) {
|
||||
// TODO 完成事件监听
|
||||
}
|
||||
|
||||
protected static String getSelfConfig(String key, String dftValue) {
|
||||
// 优先 -Dapp.id 然后是环境变量, 然后是内置的app.properties
|
||||
String value = System.getProperty(key, System.getenv(key));
|
||||
if (Strings.isBlank(value)) {
|
||||
value = selfConf.getProperty(key, dftValue);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package org.nutz.cloud.config;
|
||||
|
||||
import org.nutz.boot.AppContext;
|
||||
import org.nutz.boot.starter.ServerFace;
|
||||
import org.nutz.cloud.config.spi.ConfigureEventHandler;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
@IocBean
|
||||
public class CloudConfigureChangeStarter implements ServerFace {
|
||||
|
||||
@Inject
|
||||
protected AppContext appContext;
|
||||
|
||||
public void start() throws Exception {
|
||||
appContext.getBeans(ConfigureEventHandler.class).forEach((listener)->CloudConfig.addListener(listener));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.nutz.cloud.config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.nutz.boot.config.impl.PropertiesConfigureLoader;
|
||||
|
||||
public class CloudConfigureLoader extends PropertiesConfigureLoader {
|
||||
|
||||
public CloudConfigureLoader() {
|
||||
CloudConfig.init();
|
||||
}
|
||||
|
||||
protected void readPropertiesPath(String path) throws IOException {
|
||||
if (path.contains("/") || path.contains("\\"))
|
||||
super.readPropertiesPath(path);
|
||||
else {
|
||||
CloudConfig.fromRemote(conf, path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.nutz.cloud.config.spi;
|
||||
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
|
||||
public interface ConfigureEventHandler extends EventListener {
|
||||
|
||||
void trigger(List<KeyEvent> events);
|
||||
|
||||
class KeyEvent {
|
||||
public String name;
|
||||
public String value;
|
||||
public String action;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
org.nutz.cloud.config.CloudConfigureLoader
|
@ -0,0 +1 @@
|
||||
org.nutz.cloud.config.CloudConfigureChangeStarter
|
@ -53,6 +53,7 @@
|
||||
<module>nutzboot-starter-rabbitmq</module>
|
||||
<module>nutzboot-starter-tio</module>
|
||||
<module>nutzboot-starter-apollo-client</module>
|
||||
<module>nutzboot-starter-config-client</module>
|
||||
</modules>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
5
pom.xml
5
pom.xml
@ -649,6 +649,11 @@
|
||||
<artifactId>nutzboot-starter-apollo-client</artifactId>
|
||||
<version>${nutzboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter-config-client</artifactId>
|
||||
<version>${nutzboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
|
Loading…
Reference in New Issue
Block a user