说到配置中心,大家应该也了解目前市面上用的较多的配置中心:
百度的Disconf、Spring Cloud Config、携程的Apollo、阿里的Nacos等。
由于Disconf不再维护,以下对Spring Cloud Config、Apollo、Nacos的功能点做的对比
大家平时在开发时,项目中会用到很多配置,有些配置还和环境有关,开发、测试和生产的配置也不一样;
或者在做某一个功能时,上线后配置有可能更改,即使我们将变量写在配置中,但还是需要重新发布服务,
所以,如果有个配置中心,当更新某个配置时,可以实时生效,岂不快哉!
如果做一个分布式配置中心的产品,所需要实现的功能很多,如配置存储、配置实时推送、权限校验、版本管理&回滚、配置格式校验等。
本文介绍使用Springboot2.x+Zookeeper实现简易的分布式配置中心,使用Zookeeper存储配置,本地缓存配置,监听zookeeper的配置更新,本地实时更新。
org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE
UTF-8
1.8
2.12.0
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.apache.curator
curator-recipes
${zk.curator.version}
@Component
public class PropertiesCenter {
/**
* 配置中心
*/
Properties properties = new Properties();
CuratorFramework client = null;
TreeCache treeCache = null;
@Value("${zookeeper.url}")
private String zkUrl;
private final String CONFIG_NAME = "/config-center";
public PropertiesCenter() {
}
private void init() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.newClient(zkUrl, retryPolicy);
treeCache = new TreeCache(client, CONFIG_NAME);
}
/**
* 设置属性
* @param key
* @param value
* @throws Exception
*/
public void setProperties(String key, String value) throws Exception {
String propertiesKey = CONFIG_NAME + "/" + key;
Stat stat = client.checkExists().forPath(propertiesKey);
if(stat == null) {
client.create().forPath(propertiesKey);
}
client.setData().forPath(propertiesKey, value.getBytes());
}
/**
* 获取属性
* @param key
* @return
*/
public String getProperties(String key) {
return properties.getProperty(key);
}
@PostConstruct
public void loadProperties() {
try {
init();
client.start();
treeCache.start();
// 从zk中获取配置放入本地配置中
Stat stat = client.checkExists().forPath(CONFIG_NAME);
if(stat == null) {
client.create().forPath(CONFIG_NAME);
}
List configList = client.getChildren().forPath(CONFIG_NAME);
for (String configName : configList) {
byte[] value = client.getData().forPath(CONFIG_NAME + "/" + configName);
properties.setProperty(configName, new String(value));
}
// 监听属性值变更
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
if (Objects.equals(treeCacheEvent.getType(), TreeCacheEvent.Type.NODE_ADDED) ||
Objects.equals(treeCacheEvent.getType(), TreeCacheEvent.Type.NODE_UPDATED)) {
String updateKey = treeCacheEvent.getData().getPath().replace(CONFIG_NAME + "/", "");
properties.setProperty(updateKey, new String(treeCacheEvent.getData().getData()));
System.out.println("数据更新: "+treeCacheEvent.getType()+", key:"+updateKey+",value:"+new String(treeCacheEvent.getData().getData()));
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
@RestController
public class PropertiesController {
@Autowired
PropertiesCenter propertiesCenter;
@GetMapping("get/{key}")
public String getProperties(@PathVariable String key) {
return propertiesCenter.getProperties(key);
}
@GetMapping("set/{key}/{value}")
public String setProperties(@PathVariable String key, @PathVariable String value) throws Exception {
propertiesCenter.setProperties(key, value);
return "配置成功";
}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConfigCenterApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigCenterApplication.class, args);
}
}
增加如下内容:
server.port=8080
zookeeper.url=127.0.0.1:2181
启动成功后,测试:
浏览器访问:http://127.0.0.1:8080/set/book/java, 设置book的值为java,
再访问 http://127.0.0.1:8080/get/book,获取book的属性值。