SpringCloud进击 | 二深入:配置中心(消息总线)【Finchley版本】

1.前言

Spring Cloud Bus 为 Spring 的事件、消息总线,通过轻量消息代理连接各个分布的节点,用于在集群(例如,配置变化事件)中传播状态变化,或者其他的消息指令,可与 Spring Cloud Config 联合实现热部署。

Spring Cloud Bus 的一个核心思想是通过分布式的启动器对 Spring Boot 应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式是用 AMQP 消息代理作为通道。

Spring Cloud Bus 被国内很多都翻译为消息总线,也挺形象的。大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了 MQ 的广播机制在分布式的系统中传播消息,目前常用的有 Kafka 和 RabbitMQ。利用 Bus 的机制可以做很多的事情,其中配置中心客户端刷新就是典型的应用场景之一。

 

2.准备

  1. wei-eureka-server,端口:8090(无需修改,正常启动)
  2. wei-config-server,端口:8053(改造对象)
  3. wei-config-client 两个,端口:8063、8064(改造对象)

我们继续使用 上一节 写好的项目来做相应的改造。这里我们需要装 RabbitMQ,点击 rabbitmq 下载。也需要 curl 命令包。这些需要先在本机安装好。

 

3.进击

3.1.Config Server服务端

3.1.1.POM依赖改造

    
        
            org.springframework.cloud
            spring-cloud-config-server
        

        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

        
            org.springframework.cloud
            spring-cloud-bus
        

        
            org.springframework.cloud
            spring-cloud-stream-binder-rabbit
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

3.1.2.配置文件改造

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定进行服务注册的地址。高可用配置中心-服务端服务化
server:
  port: 8053
spring:
  application:
    name: wei-config-server
  cloud:
    config:
      label: master    # 对应 Git 上不同的分支,默认为 master
      server:
        git:
          username:
          password:
          uri: https://github.com/itanping/wei-springcloud    # 配置 Git 仓库地址
          search-paths: wei-config/config-profile             # Git仓库地址下的相对地址,可以配置多个,用,分割
    bus:
      enabled: true
      trace:
        enabled: false
management:
  endpoints:
    web:
      exposure:
        include: bus-refresh

3.1.3.启动类

启动类与上一节相同,无需改造。

package com.wei;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

/**
 * Config Server
 * 注解@EnableConfigServer,开启对配置中心的支持
 */
@SpringBootApplication
@EnableConfigServer
public class WeiConfigServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(WeiConfigServerApplication.class, args);
    }
}

 

到此,Config Server 服务端改造完成。

启动。


 

3.2.Config Client客户端

3.2.1.POM依赖改造

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.cloud
            spring-cloud-starter-config
        

        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

        
        
            org.springframework.cloud
            spring-cloud-bus
        
        
            org.springframework.cloud
            spring-cloud-stream-binder-rabbit
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.cloud
            spring-cloud-starter-bus-amqp
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

3.2.2.配置文件改造

还是分为两个配置文件,分别如下:

bootstrap.yml

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定进行服务注册的地址。高可用配置中心实现
spring:
  application:
    name: wei-config-client
  cloud:
    config:
#      uri: http://localhost:8053     # 配置服务中心的具体地址,即 config-server
      name: config-client           # 对应配置文件名 config-client-dev.properties 的 {application} 部分
      profile: dev                  # 对应配置文件名 config-client-dev.properties 的 {profile} 部分
      label: master                 # 使用 {label} 对应 Git 的分支名,如果配置中心使用的是本地存储,则该参数无用
      discovery:
        enabled: true                    # 开启 Config 服务发现支持
        service-id: wei-config-server    # 高可用配置中心实现需要将uri配置去掉,使用service-id直接指向Server端地址
    bus:
      trace:
        enabled: true
      enabled: true

application.yml

server:
  port: 8063
spring:
  rabbitmq:
    addresses: localhost
    port: 5672     # client端通信口
    username: admin
    password: admin

配置 RabbitMQ,默认端口:

  • 5672:client 端通信口
  • 15672:管理界面UI端口

如果设置的是 RabbitMQ 上新建用户,则需要确保该用户的 “Can access virtual hosts” 项不为 “No access”,即 “Current permissions” 不能为 “... no permissions ...”。否则,使用新建用户作为测试配置时,运行启动类时会报 java.net.SocketException: Socket Closed 异常。

3.2.3.Controller改造

package com.wei.controller.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 注解@RefreshScope必须加,否则客户端会收到服务端的更新消息,但是更新不了,因为不知道更新哪里的
 */
@RestController
@RefreshScope
public class DemoController {

    @Value("${config.env:env parameter error!}")
    private String configEnv;

    @Value("${config.tip:tip parameter error!}")
    private String configTip;

    @GetMapping("/demo/info")
    public String hello() {
        String result = "[Config Client] env:" + configEnv + ", tip:" + configTip;
        return result;
    }
}

注解@RefreshScope必须加,否则客户端会受到服务端的更新消息,但是更新不了,因为不知道更新哪里的。

3.2.4.启动类

启动类与上一节相同,无需改造。

package com.wei;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;

@SpringBootApplication
public class WeiConfigClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(WeiConfigClientApplication.class, args);
    }
}

 

到此,Config Client 客户端改造完成。

启动。

 

4.测试

确认已经分别依次启动以下模块:

  1. wei-eureka-server,端口:8090
  2. wei-config-server,端口:8053
  3. wei-config-client 两个,端口:8063、8064

启动完成后,可以看看 RabbitMQ 管理界面(http://localhost:15672/),RabbitMQ 会自动创建一个 topic 类型的 Exchange 和两个以 springCloudBus.anonymous. 开头的匿名 Queue。

SpringCloud进击 | 二深入:配置中心(消息总线)【Finchley版本】_第1张图片 【RabbitMQ】

访问:http://localhost:8063/demo/info,http://localhost:8064/demo/info,返回信息相同:

[Config Client] env:dev, tip:Demo for Spring Cloud config 2018-12-25

 

将 Git 中的配置 tip 值改为 config.tip=Demo for Spring Cloud config 2018-12-25 + bus

再访问:http://localhost:8063/demo/info,http://localhost:8064/demo/info,返回信息相同:

[Config Client] env:dev, tip:Demo for Spring Cloud config 2018-12-25

结果还是一样,也就是不管怎么刷新,都没有拿到变更后的内容。

 

这样,CMD 下执行命令,进行服务端刷新

curl -X POST http://localhost:8053/actuator/bus-refresh/

或者使用 Postman 等工具,发送 http://localhost:8053/actuator/bus-refresh/ POST请求,返回 204 No Content 即OK。

然后再次访问:http://localhost:8063/demo/info,http://localhost:8064/demo/info

[Config Client] env:dev, tip:Demo for Spring Cloud config 2018-12-25 + bus

说明,对服务端使用 /actuator/bus-refresh 刷新后,可以获取到变更后的最新 Git 配置。

到这里,我们已经能够通过 Spring Cloud Bus 来实时更新总线上的属性配置了。

 

那如果对客户端使用 /actuator/bus-refresh 会发生什么呢?是只刷新了当前客户端还是刷新全部客户端,还是一个都没刷新呢?

不妨一试:

改造客户端 bootstrap.yml,添加以下配置,把客户端上的 bus-refresh 端点给放出来:

management:
  endpoints:
    web:
      exposure:
        include: bus-refresh

重启客户端,再访问:http://localhost:8063/demo/info 和 http://localhost:8064/demo/info,结果分别是原来的值:

[Config Client] env:dev, tip:Demo for Spring Cloud config 2018-12-25 + bus

 

将 Git 中的配置 tip 改为 config.tip=Demo for Spring Cloud config 2018-12-25 + BUS

同样的,CMD 下 执行以下命令,进行客户端刷新

curl -X POST http://localhost:8063/actuator/bus-refresh/

从后台日志可以看到:

客户端刷新后,有了新的URL映射:

SpringCloud进击 | 二深入:配置中心(消息总线)【Finchley版本】_第2张图片客户端刷新

也会重新读取配置文件:

SpringCloud进击 | 二深入:配置中心(消息总线)【Finchley版本】_第3张图片客户端刷新】

然后再次访问:http://localhost:8063/demo/info 和 http://localhost:8064/demo/info

[Config Client] env:dev, tip:Demo for Spring Cloud config 2018-12-25 + BUS

说明只要开启 Spring Cloud Bus 后,不管是对 Config Server 还是 Config Client 执行 /actuator/bus-refresh 都是可以更新配置的。

 

5.总结

5.1.原理分析

通过使用 Spring Cloud Bus 与 Spring Cloud Config 的整合,并以 RabbitMQ 作为消息代理,实现了应用配置的动态更新。

SpringCloud进击 | 二深入:配置中心(消息总线)【Finchley版本】_第4张图片 【Spring Cloud Bus】

整个方案的架构如上图所示,其中包含了Git仓库、Config Server、以及微服务“Service A”的两个实例(Config Client),这两个实例中都引入了 Spring Cloud Bus,所以他们都连接到了 RabbitMQ 的消息总线上。

当我们将系统启动起来之后,“Service A”的两个实例会请求 Config Server 以获取配置信息,Config Server 根据应用配置的规则从Git仓库中获取配置信息并返回。

此时,若我们需要修改“Service A”的属性。首先,通过Git管理工具去仓库中修改对应的属性值,但是这个修改并不会触发“Service A”实例的属性更新。我们向“Service A”的其中一个实例(8063)发送POST请求,访问 /actuator/bus-refresh 接口。

此时,“Service A”的实例(8063)就会将刷新请求发送到消息总线中,该消息事件会被“Service A”的实例8063和实例8064从总线中获取到,并重新从 Config Server 中获取它们的配置信息,从而实现配置信息的动态更新。

这个架构,简而言之,就是服务的配置更新需要通过向具体服务中的某个实例发送请求,再触发对整个服务集群的配置更新。

 

这样的架构虽然能实现功能,但是结果是,我们指定的应用实例就会不同于集群中的其他应用实例,这样会增加集群内部的复杂度,不利于将来的运维工作。

我们需要优化这个架构,稍微调整一下:

SpringCloud进击 | 二深入:配置中心(消息总线)【Finchley版本】_第5张图片 【Spring Cloud Bus】

调整点如下:

  • 在 Config Server 中也引入 Spring Cloud Bus,将配置服务端也加入到消息总线中来
  • /actuator/bus-refresh 请求不在发送到具体服务实例上,而是发送给 Config Server

通过上面的优化,我们的服务实例就不需要再承担触发配置更新的职责。同时,对于 Git 的触发等配置都只需要针对 Config Server 即可,从而简化了集群上的一些维护工作。即我们这节所实现的内容。

 

是的,如果你也同步操作到了这里,你会发现你已经完成并优化了这个架构。

 

5.2.参考

https://springcloud.cc/spring-cloud-bus.html
http://blog.didispace.com/springcloud7/
https://windmt.com/2018/04/19/spring-cloud-9-config-eureka-bus/
http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html#_spring_cloud_bus

 

6.系列

SpringCloud进击 | 一浅出:服务注册与发现(Eureka)【Finchley版本】
SpringCloud进击 | 二浅出:服务消费者(Ribbon+REST)【Finchley版本】
SpringCloud进击 | 三浅出:服务消费者(Feign)【Finchley版本】
SpringCloud进击 | 四浅出:断路器与容错(Hystrix)【Finchley版本】
SpringCloud进击 | 五浅出:服务网关 - 路由(Zuul Router)【Finchley版本】
SpringCloud进击 | 六浅出:服务网关 - 过滤器(Zuul Filter)【Finchley版本】
SpringCloud进击 | 七浅出:配置中心(Git配置与更新)【Finchley版本】
SpringCloud进击 | 一深入:配置中心(服务化与高可用)【Finchley版本】
SpringCloud进击 | 二深入:配置中心(消息总线)【Finchley版本】
SpringCloud进击 | 三深入:服务链路跟踪(Spring Cloud Sleuth)【Finchley版本】
SpringCloud进击 | 四深入:服务链路跟踪(Sleuth+Zipkin+RabbitMQ整合)【Finchley版本】
SpringCloud进击 | 五深入:断路器监控(Hystrix Dashboard)【Finchley版本】
SpringCloud进击 | 六深入:断路器聚合监控(Hystrix Turbine)【Finchley版本】
SpringCloud进击 | 七深入:高可用的服务注册中心【Finchley版本】


下一节,请继续关注:SpringCloud进击 | 三深入:服务链路跟踪(Spring Cloud Sleuth)【Finchley版本】


 

你可能感兴趣的:(Spring,Cloud,SpringCloud,Java,Spring,Cloud,Bus,消息总线)