TA的每日心情 | 擦汗 4 天前 |
---|
签到天数: 1042 天 连续签到: 4 天 [LV.10]测试总司令
|
要搭建一套微服务管理平台,根据微服务管理12项原则,微服务应用代码和应用配置要分开管理和部署。通常的做法是实现一套配置管理中心。业界有挺多流行的配置管理中心。比如Nacos,Apollo,Spring Could Config等。今天为大家介绍一种利用Spring Cloud 家族中的Spring Cloud Config Bus加上RocketMQ 搭建配置管理中心并实现动态刷新。原创不易。喜欢的点赞收藏哈。
整体架构
首先看下整体架构,在这里多说一句,在实现一套系统之前一定要把架构设计好,在前期设计过程中尽可能的避免一些因为缺少设计导致的后面的繁重的重构工作。那么一个配置管理中心的几个要素要先梳理清楚:
配置管理服务端:承载配置管理,配置动态下发,配置拉取等功能。
应用客户端:启动依赖远程配置的微服务应用。
中间件:配置刷新依赖的中间件,这里我们选择RocketMQ。
配置存储介质:配置内容存储的地方,一般是代码仓库或者数据库,这里我们选择MySql数据库存储配置。
配置管理中心
平台管理员(一般是运维人员)通过配置管理服务管理应用配置内容,应用启动通过Restful请求从而配置管理服务拉取远程配置,当配置需要更改时,通过配置管理服务端触发动态刷新,配置内容以RocketMQ消息形式被应用客户端消费并更新应用上下文,无需重启应用。
实现细节
配置服务端添加以下maven依赖,建议根据情况自定义rocketmq-client版本:
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>
- <version>${spring.cloud.bus.rocketmq.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.rocketmq</groupId>
- <artifactId>rocketmq-client</artifactId>
- <version>${rocketmq.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
复制代码 spring cloud bus已经实现了一些默认的event, 本质上是RocketMQ消息来实现server和client端通信来动态刷新配置,如果想要应用能够启动的时候从远程拉取配置,服务端需要引入下面依赖。
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-config-server</artifactId>
- <version>${spring-cloud.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
复制代码 服务端配置如下:
- server.port=8080
- spring.application.name=config-server
- spring.cloud.bus.enabled=true
- spring.cloud.bus.destination=springCloudBus
- rocketmq.name-server=127.0.0.1:9876
复制代码 然后客户端需要添加下面的依赖:
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-config-client</artifactId>
- <version>${spring-cloud.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
复制代码 客户端配置application.propeties在resource目录下:
- spring.application.name=config-client-app
- server.port=8090
- rocketmq.name-server=127.0.0.1:9876
复制代码 客户端配置bootstap.properties在resource目录下,spring cloud config client会优先根据bootstrap配置从远程加载应用配置。
- spring.application.name=config-client-app
- //接口URL可以是config server自己实现的接口地址
- spring.cloud.config.server.uri=http://127.0.0.1/dev/config-client-app
- spring.cloud.config.server.username=接口认证账号
- spring.cloud.config.server.password=接口认证密码
复制代码 服务端发布事件
由于 Spring Cloud Bus传输消息基于 pub&sub 模型,因此我们需要定义一个反映应用程序配置修改的事件。该事件需要是 RemoteApplicationEvent 的子类,它将被 spring cloud bus 注册到 spring 上下文中。我们可以为实例化事件创建一个特定的endpint,并通过ApplicationEventPublisher.class 发布事件,以便将相应的事件消息发送到RocketMQ 消息通道。
- public class ConfigRemoteApplicationEvent extends RemoteApplicationEvent {
- private Map<String, String> configMap;
-
- public ConfigRemoteApplicationEvent() {
- this.configMap = null;
- }
-
- public ConfigRemoteApplicationEvent(Object source, String originService, String destinationService, Map configMap) {
- super(source, originService, destinationService);
- this.configMap = configMap;
- }
-
- public Map<String, String> getConfigMap() {
- return configMap;
- }
-
- public void setConfigMap(Map<String, String> configMap) {
- this.configMap = configMap;
- }
- }
复制代码 客户端监听事件
应用客户端一旦接收到来自 RocketMQ 的事件消息,就可以相应地处理事件并刷新配置。有两种方式来处理远程应用程序事件,第一种是定义一个自定义的事件监听器,它继承了 ApplicationListener.class 并实现了 onApplicationEvent 方法。
- @Component
- public class ConfigRemoteApplicationEventListener implements ApplicationListener<ConfigRemoteApplicationEvent> {
- @Override
- public void onApplicationEvent(ConfigRemoteApplicationEvent configRemoteApplicationEvent) {
- System.out.printf("Received message %s", configRemoteApplicationEvent.getConfigMap().toString());
- }
- }
复制代码 另一种是方法上加上@EventListner注解:
- @EventListener
- public void onConfigEvent(ConfigRemoteApplicationEvent event){
- Map<String, String> configs = event.getConfigMap();
- System.out.printf("Server received config to updated : %s\n",event.getConfigMap().toString());
- Iterator configIterator = configs.entrySet().iterator();
-
- while(configIterator.hasNext()) {
- Map.Entry<String, String> entry = (Map.Entry)configIterator.next();
- environmentManager.setProperty(entry.getKey(), entry.getValue());
- }
- }
复制代码 结束!
|
|