Springboot2.x+Zookeeper实现分布式配置中心

0、市面上的配置中心产品

说到配置中心,大家应该也了解目前市面上用的较多的配置中心:
百度的Disconf、Spring Cloud Config、携程的Apollo、阿里的Nacos等。
由于Disconf不再维护,以下对Spring Cloud Config、Apollo、Nacos的功能点做的对比

分布式配置中心

分布式配置中心

大家平时在开发时,项目中会用到很多配置,有些配置还和环境有关,开发、测试和生产的配置也不一样;
或者在做某一个功能时,上线后配置有可能更改,即使我们将变量写在配置中,但还是需要重新发布服务,
所以,如果有个配置中心,当更新某个配置时,可以实时生效,岂不快哉!
如果做一个分布式配置中心的产品,所需要实现的功能很多,如配置存储、配置实时推送、权限校验、版本管理&回滚、配置格式校验等。

本文介绍使用Springboot2.x+Zookeeper实现简易的分布式配置中心,使用Zookeeper存储配置,本地缓存配置,监听zookeeper的配置更新,本地实时更新。

1、引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<zk.curator.version>2.12.0</zk.curator.version>
</properties>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zk.curator.version}</version>
</dependency>


</dependencies>

2、配置中心类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@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<String> 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();
}
}
}

3、测试的set/get接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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 "配置成功";
}
}

4、启动类

1
2
3
4
5
6
7
8
9
10
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);
}
}

5、配置application.properties

增加如下内容:

1
2
server.port=8080 
zookeeper.url=127.0.0.1:2181

6、首先启动本地的zookeeper,再启动ConfigCenterApplication

启动成功后,测试:

浏览器访问:http://127.0.0.1:8080/set/book/java, 设置book的值为java,

再访问 http://127.0.0.1:8080/get/book,获取book的属性值。


Springboot2.x+Zookeeper实现分布式配置中心
https://river106.cn/posts/dde36008.html
作者
river106
发布于
2020年4月27日
许可协议