文章目录
  1. 1. springCloud通过cloudBus实现config动态刷新
    1. 1.0.1. config-server
      1. 1.0.1.1. config-server配置文件
    2. 1.0.2. eureka
      1. 1.0.2.1. eureka配置文件
    3. 1.0.3. app-service
      1. 1.0.3.1. app-service配置文件
      2. 1.0.3.2. 如何触发读取已变更的配置文件
      3. 1.0.3.3. springConfigServer的问题
    4. 1.0.4. Spring Cloud Bus

springCloud通过cloudBus实现config动态刷新

springCloud中通过spring-config组件,可以实现配置文件外置在git仓库中,如果某个服务需要更改配置文件,也可以做到不重启服务,刷新此服务的配置文件。

config-server

spring-config-server 项目的
pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>config-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>config-server</name>
    <description>config-server</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.10</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.10</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10</version>
        </dependency>

  

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>

ConfigServerApplication.java:

@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
@ComponentScan(basePackages = "com.example.controller")
public class ConfigServerApplication {

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


}

config-server配置文件

配置文件application.yml:

server:
  port: 8883
  servlet:
    context-path: /

spring:
  application:
      name: config-server
  profiles:
      active: local
  cloud:
    config:
      server:
        git:
          uri: http://gitlab.rios.com:8080/servers/config-repo.git
          clone-on-start: true
          username: sam.hu
          password: 12345678
          default-label: master

eureka:
  instance:
    hostname: localhost
    prefer-ip-address: true
    instance-id: ${eureka.instance.hostname}:${server.port}
    nonSecurePort: ${server.port}
  client:
    service-url:
      defaultZone: http://localhost:9999/eureka
    healthcheck:
          enabled: true



management:
  endpoints:
    web:
      exposure:
        include: '*'

eureka

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>eureka</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>eureka</name>
    <description>Demo project for Spring Boot(spring cloud)</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

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


        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.10</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.10</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10</version>
        </dependency>

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

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>

eureka配置文件

bootstrap.xml:

server:
  port: 9999
  servlet:
    context-path: /

spring:
  application:
    name: eureka


eureka:
  instance:
    hostname: localhost
    prefer-ip-address: false
    nonSecurePort: ${server.port}
  client:
    service-url:
      defaultZone: http://127.0.0.1:9999/eureka
    register-with-eureka: false

app-service

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>appservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>appservice</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.10</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.10</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10</version>
        </dependency>


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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

app-service配置文件

server:
    port: 9000

spring:
  application:
      name: appservice
  profiles:
      active: local
  cloud:
      config:
        label: master
        uri: http://localhost:8883

eureka:
    instance:
        prefer-ip-address: true
        nonSecurePort: 80
    client:
        service-url:
            defaultZone: http://localhost:9999/eureka
        register-with-eureka: true



management:
  endpoints:
    web:
      exposure:
        include: '*'

AppServiceApplication.java:

@SpringBootApplication
@EnableEurekaClient
@ComponentScan("com.example.controller")
public class AppServiceApplication {

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

}

为了测试动态配置文件的有效性,我写了一个测试Controller入口
HelloController.java:

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by sam on 2018/4/30.
 */
@RestController
@RefreshScope
@Order(value = 100)
public class Hello2Controller {


    @Value("${server.port}")
    String port;

    @Value("${turn.switch}")
    private String turnSwitch;

    @RequestMapping("/hello")
    public String home(@RequestParam String name) {
        return
                "hello "+name+",i am from port:" +port + ",appName :"+ turnSwitch;
    }

}

在远程仓库中添加一个appservice-local.yml的配置文件:

turn:
    switch: on

全部启动,访问http://localhost:9999发现config-server和appservice都注册到eureka上了。

我现在访问一下appservice的/hello接口:

curl http://localhost:9000/hello?name=sam

返回:

hello sam, i am from port: 9000,turnSwitch: true 

我现在将appservice-local.yml的配置文件改为:

turn:
      switch: off
      

访问config-server先看看此服务返回的最新配置:

curl http://localhost:8883/appservice/local

发现返回的turn.switch: false

那我看看appservice那个/hello接口是否返回的是最新的值:

curl http://localhost:9000/hello?name=jack

发现返回的是:

hello jack, i am from port: 9000,turnSwitch: true

值没有改变。

如何触发读取已变更的配置文件

因为我们在配置文件配置如下内容:

management:
  endpoints:
    web:
      exposure:
        include: '*'
    
    

把所有的actuator组件的接口都暴露出来,包括config的刷新接口,所以,针对单服务,我只要请求:

curl http://localhost:9000/actuator/refresh

appservice服务的配置就更新了。

再看看/hello接口:

hello sam, i am from port: 9000,turnSwitch: false

springConfigServer的问题

springConfigServer只能解决单实例微服务的问题,如果我的微服务是集群部署的,那么我要更新配置的话,是不是要对每台微服务实例进行:

    http://IP:port/actuator/refresh

这样请求效率很低,而且很傻,很low。

有没有办法,我配置文件更新了,只需要访问一次刷新请求,我所有引用的配置文件都能实时动态更新。
答案是,全家桶有解决方案,它就是Spring Cloud Bus!!!

Spring Cloud Bus

Spring cloud bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring bus的一个核心思想是通过分布式的启动器对spring boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。

目前唯一实现的方式是用AMQP消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。

大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。利用bus的机制可以做很多的事情,其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述bus在配置中心使用的机制。

paste image

以上图片有点问题,就是请求bus的地址在springboot 2.0 之后已经变成:http://localhost:8883/actuator/bus-refresh/{目标微服务}

目前网络上大部分是RabbitMQ用来实现的bus,本人将kafka的实现来集成bus。

只需要将config-server中的pom.xml加入:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-kafka</artifactId>
 </dependency>

并且配置文件改为:

server:
  port: 8883
  servlet:
    context-path: /

spring:
  application:
      name: config-server
  kafka:
      bootstrap-servers: 127.0.0.1:9092
      consumer:
          group-id: kafka-bus
  profiles:
      active: local
  cloud:
    config:
      server:
        git:
          uri: http://gitlab.rios.com:8080/servers/config-repo.git
          clone-on-start: true
          username: sam.hu
          password: 12345678
          default-label: master
    bus:
      enabled: true
      trace:
        enabled: true
      refresh:
        enabled: true


eureka:
  instance:
    hostname: localhost
    prefer-ip-address: true
    instance-id: ${eureka.instance.hostname}:${server.port}
    nonSecurePort: ${server.port}
  client:
    service-url:
      defaultZone: http://localhost:9999/eureka
    healthcheck:
          enabled: true



management:
  endpoints:
    web:
      exposure:
        include: '*'

重后启动下config-server。

注意事项

  1. 必须保证kafka的自动生成topic没有被禁用
  2. 必须保证配置中心的配置中,配置了kafka消费组,其它客户端没有配置consumer的group-id配置
  3. bus使用的是默认的spring

如果这两个问题中的任何一个没有得到保证,则全局推送就会无法收到消费,进而不知道需要更新,

除了配置中心需要改造,appservice客户端服务也需要改造, pom.xml加入:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>

配置文件为:

server:
    port: 9000

spring:
  application:
      name: appservice
  profiles:
      active: local
  cloud:
      config:
        label: master
        uri: http://localhost:8883
      bus:
        refresh:
          enabled: true
  kafka:
      bootstrap-servers: 127.0.0.1:9092


eureka:
    instance:
        prefer-ip-address: true
        nonSecurePort: 80
    client:
        service-url:
            defaultZone: http://localhost:9999/eureka
        register-with-eureka: true



management:
  endpoints:
    web:
      exposure:
        include: '*'

启动两个appservice实例,端口号分别是9000,9001,注册到eureka。

然后我再将repo里的appservice-local.yml文件修改了一遍,将值从off改为on。

我希望调用一个接口,可以刷新所有引用配置。

现在只需执行:

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

是的,全局刷新配置。

针对某个服务进行刷新:

curl -X POST http://localhost:8883/actuator/bus-refresh/{微服务名称}

然后我们分别访问9000和9001的hello接口,则返回的值是已经改过的。

大功告成!!!!!

文章目录
  1. 1. springCloud通过cloudBus实现config动态刷新
    1. 1.0.1. config-server
      1. 1.0.1.1. config-server配置文件
    2. 1.0.2. eureka
      1. 1.0.2.1. eureka配置文件
    3. 1.0.3. app-service
      1. 1.0.3.1. app-service配置文件
      2. 1.0.3.2. 如何触发读取已变更的配置文件
      3. 1.0.3.3. springConfigServer的问题
    4. 1.0.4. Spring Cloud Bus