embedconfig
项目介绍
一个嵌入式的在线配置系统,和其他分布式配置中心不一样的是,它使用一个servlet的方式集成到javaWeb项目里面,无需中心的配置服务即可实现配置文件热发。他适合给独立分发的各种javaWeb系统提供在线配置的能力,当然我也会在后续考虑如何集成集中式配置中心的实现以及如何无缝切换
软件架构
软件架构说明
安装教程
1. 引入项目依赖(通过maven或者gradle)
com.virjar
embed-config
1.0-SNAPSHOT
2. 配置embedConfig servlet
2.1 普通javaEE项目
在打war包的情况下,我们可以编辑web.xml,此时在web.xml中增加如下配置:
embedConfig
com.virjar.embedconfig.servlet.EmbedConfigServlet
embedConfig
/embedConfig/*
2.2 springboot项目
如果springboot项目中残留web.xml,也可以在web.xml中配置,如果springboot项目中没有web.xml,则可以在启动类中增加如下配置:
/**
* Hello world!
*/
@SpringBootApplication
public class App extends SpringBootServletInitializer {
@Bean
public ServletRegistrationBean EmbedConfigServlet() {
//在这里注册EmbedConfigServlet的入口
return new ServletRegistrationBean(new EmbedConfigServlet(), "/embedConfig/*");
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(App.class);
}
}
2.3 访问在线链接
2.1或者2.2两种方案配置好之后,可以直接在浏览器中打开embedConfig的页面了,访问路径为:http(s):host/embedConfig
3.使用说明
3.1 配置文件入口设置
您需要设置自己的配置文件路径,embedConfig将会将指定文件夹下面的所有文件映射到系统。目前文件路径设置通过全局配置文件的方式,如下:
在classpath下面创建一个配置文件embed-config.properties
增加如下内容:
#配置文件地址,大多数情况该配置是需要被重写的,使用springboot的场景大多数情况不需要重写
embed-config.configDir=~/.embedConfig/
#缓存地址,这个文件夹存储文件的历史记录
embed-config.cacheDir=~/.embedConfigCache/
如果你没有特定的配置文件映射目录,可以使用默认。
3.2 在代码中使用
以下代码,通过给某个文件添加监听事件,可以在文件发生变动的时候,通过您注册的监听函数得到变更通知和变更内容
public class ClassPathPropertiesTest {
public static void main(String[] args) throws InterruptedException {
MapConfig.get("classPathPropertiesTest.properties").addConfigListener(new ConfigListener>() {
@Override
public void onLoad(Map model) {
System.out.println(model);
}
});
Thread.sleep(100000);
}
}
以下代码,可以在任何时候,得到最新的配置内容
//读取classPathPropertiesTest.properties文件中,key为theKey的属性的值
String theValue = MapConfig.get("classPathPropertiesTest.properties").getConfig().get("theKey");
以下代码,自定义了文件内容。embedConfig默认支持*.properties,*.xml,*.json 和palinText四种文件格式
/**
* Created by virjar on 2018/6/5.
*/
class Person {
String name;
int age;
}
public class CustomFileType extends AbstractConfiguration {
private static ConcurrentMap allMapConfigs = Maps.newConcurrentMap();
public static CustomFileType get(String fileName) {
CustomFileType mapConfig = allMapConfigs.get(fileName);
if (mapConfig != null) {
return mapConfig;
}
synchronized (CustomFileType.class) {
mapConfig = allMapConfigs.get(fileName);
if (mapConfig != null) {
return mapConfig;
}
allMapConfigs.putIfAbsent(fileName, new CustomFileType(fileName));
return allMapConfigs.get(fileName);
}
}
protected CustomFileType(String fileName) {
super(fileName);
}
@Override
public Person parse(String data) {
String[] split = data.split("&&");
Person person = new Person();
person.name = split[0];
person.age = Integer.parseInt(split[1]);
return person;
}
public static void main(String[] args) {
//获取thePeron.txt的最新文件内容,并且将它转化为一个java对象
Person person = CustomFileType.get("thePeron.txt").getConfig();
//监听thePeron.txt的文件内容,在文件内容发生变动的时候,接受通知
CustomFileType.get("thePeron.txt").addConfigListener(new ConfigListener() {
@Override
public void onLoad(Person model) {
System.out.println("配置文件发生了变更,新的内容为:" + model.toString());
}
});
}
}
4.二次开发
4.1 构建前端UI
前端UI使用vue书写,follow frontend项目下面的README.md即可将前端代码发布到java工程的资源目录下面
npm install 安装相关依赖
npm run build 发布前端代码到java工程的资源目录
4.2 前端UI项目的dev环境
UI项目将会调用后端接口,所以需要开启后端接口,在开启前端的dev环境。相关依赖已经配好。后端接口启动在8080,前端代码启动在8081
启动后端项目,运行代码:embedconfig/embedConfigTest/src/main/java/com/virjar/App.java
安装前端代码相关依赖npm install,如果已经安装,则不需要重新安装
启动前端代码dev环境: npm run dev,
打开网页查看前端:http://localhost:8081/
4.2 增加新的接口
embedConfig没有使用strut或者SpringMVC或者jfinal,为了最大兼容各类框架,embedConfig通过servlet进行扩展。所以embedConfig二次开发的时候,url mapping规则
无法做到这些成熟框架那样完善和强大,embedConfig内置一个miniController进行url的内部路由,你需要手动注册你的url handler。MiniController只支持确定的URL前缀路由,
handler数据返回格式只能是application/json;utf-8,新接口开发流程:
创建handler:
public class FileContentController implements RequestHandler {
@Override
public JSONObject handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpResponse, String path) {
//在这里实现业务逻辑
String fileName = httpServletRequest.getParameter("fileName");
String version = httpServletRequest.getParameter("version");
if (StringUtils.isBlank(fileName)) {
return ReturnUtils.returnFailed("required parameter: fileName");
}
ConfiguredFile configuredFile = ConfiguredFile.createOrGet(fileName);
List historyVersions = configuredFile.getHistoryVersions();
if (historyVersions.isEmpty()) {
return ReturnUtils.returnFailed("file not exist");
}
FileVersion fileVersion;
if (StringUtils.equalsIgnoreCase(version, "undefined")) {
version = "";
}
if (StringUtils.isEmpty(version)) {
fileVersion = configuredFile.getNowFileVersion();
} else {
fileVersion = configuredFile.findVersion(Long.parseLong(version));
}
if (fileVersion == null) {
return ReturnUtils.returnFailed("version not exist");
}
return ReturnUtils.returnSuccess(fileVersion.fileContent());
}
//这里需要表明该handler用来处理那个url
@Override
public String urlMapping() {
return "embedConfig/content";
}
}
注册handler到MiniController
MiniController.getInstance().addRequestMapping(new FileContentController());
前后端构建全流程
构建前端代码: 在frontend项目根目录执行命令npm run build
构建后端jar包:在java项目根目录执行命令mvn install -Dmaven.test.skip=true
在本地maven仓库或者target目录即可看到输出的jar包
参与贡献
Fork 本项目
新建 Feat_xxx 分支
提交代码
新建 Pull Request
码云特技
使用 Readme_XXX.md 来支持不同的语言,例如 Readme_en.md, Readme_zh.md
GVP 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
api接口
{
"data": ["classPathPropertiesTest.properties"],
"message": "success",
"status": 0
}
{
"data": {
"fileName": "classPathPropertiesTest.properties",
"history": [{
"sign": "746675774f6a7f447c6e7d7fffffff826e7affffff8475427b45474920000000000",
"realPath": "/Users/virjar/.embedConfigCache/classPathPropertiesTest.properties.1",
"version": 1
}, {
"sign": "746675774f6a7f447c6e7d7fffffff826e7affffff84754200000000000000",
"realPath": "/Users/virjar/.embedConfigCache/classPathPropertiesTest.properties.0",
"version": 0
}]
},
"message": "success",
"status": 0
}
{
"data": "testKey=testvalue1i234\n",
"message": "success",
"status": 0
}
{
"data": "testKey=testvalue1",
"message": "success",
"status": 0
}
{
"data": "",
"message": "success",
"status": 0
}
注意,index.html的页面地址位于http://127.0.0.1:8080/embedConfig/index.html,api接口开发基于该index.html相对地址开发