Spring Cloud微服务(二): 配置中心

空扰寡人 提交于 2019-12-24 16:40:06

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

pring Cloud Config为分布式系统中的外部化配置提供服务器和客户端支持。

Spring Cloud Config原理

创建Spring Cloud Config 服务端

依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/>
  </parent>
  <groupId>proj.ms</groupId>
  <artifactId>config</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <name>config</name>
  <description>config center</description>

  <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-config-monitor</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-config-server</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.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </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>

</project>


配置说明

  • spring.cloud.config.server.git 使用Git对配置文件进行管理。uri指定Git仓库地址。
  • spring.rabbitmq 使用rabbitmq作为消息队列,用于配置的自动刷新。

添加注解

在启动类上添加@SpringBootApplication、@EnableDiscoveryClient、@EnableConfigServer三个注解:

@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
@ServletComponentScan
public class ConfigApplication {

    public static void main(String[] args) {

        SpringApplication.run(ConfigApplication.class, args);
    }

}

说明

  • @EnableDiscoveryClient 将Spring Cloud Config 服务端作为Eureka的客户端。Spring Cloud Config作为微服务在Eureka中注册,其它服务可通过服务名直接访问配置中心。
  • @EnableConfigServer 标识此应用为ConfigServer,启用默认配置。
  • @ServletComponentScan 扫描Servlet组件,将自定义过滤器添加至Spring中。

添加配置

spring:
  application:
    name: config
  cloud:
    config:
      server:
        git:
          uri: "http://gitlab.figring.com/ms/config.git"
          username: "config-center"
          password: "W&Yd)R3Vf"
  rabbitmq:
    host: 192.168.75.130
    port: 5672
    username: guest
    password: guest
eureka:
  client:
    service-url:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/,http://peer3:8763/eureka/
    fetch-registry: true
    registry-fetch-interval-seconds: 8
  instance:
    lease-renewal-interval-in-seconds: 4
    lease-expiration-duration-in-seconds: 12
server:
  port: 8888
management:
  endpoints:
    web:
      exposure:
        include: "*"

配置说明

  • spring.cloud.config.server.git 使用Git对配置文件进行管理。uri指定Git仓库地址。
  • spring.rabbitmq 使用rabbitmq作为消息队列,用于配置的自动刷新。

添加GitLab的Web Hook

在GitLab中添加Spring Cloud Config 服务端配置刷新的回调地址。上述配置文件中配置的GitLab仓库若发生push event,GitLab将调用此地址通知配置中心。配置中心暴露的端点为/actuator/bus-refresh,对应源码中的RefreshBusEndpoint类。

修改GitLab的Web Hook的Request Body

GitLab调用/actuator/bus-refresh端点,在配置中心会发生Json序列化异常。通过拦截bus-refresh端点并进行自定义修改Request Body以达到正常调用的目的。

/**
 * 用于拦截bus-refresh端点的请求
 */
@WebFilter(filterName = "BusRefreshFilter", urlPatterns = "/*")
public class BusRefreshFilter implements Filter {

    public static final String BUS_REFRESH_ENDPOINT = "/bus-refresh";

    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
        FilterChain filterChain)
        throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String url = httpServletRequest.getRequestURI();
        // 只过滤/bus-refresh端点
        if (!url.endsWith(BUS_REFRESH_ENDPOINT)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        //修改Request Body
        CustometRequestWrapper requestWrapper = new CustometRequestWrapper(httpServletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    @Override
    public void destroy() {

    }

}
/**
 * Request包装类
 */
public class CustometRequestWrapper extends HttpServletRequestWrapper {

    public CustometRequestWrapper(HttpServletRequest request) {

        super(request);
    }

    @Override
    public ServletInputStream getInputStream() {

        byte[] bytes = new byte[0];
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);

        return new ServletInputStream() {

            @Override
            public boolean isFinished() {

                return byteArrayInputStream.read() == -1;
            }

            @Override
            public boolean isReady() {

                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() {

                return byteArrayInputStream.read();
            }
        };
    }

}

启动Spring Cloud Config 服务端

启用应用程序。

客户端应用

添加客户端配置文件

在Git仓库中创建user-service-dev.ym文件,添加以下内容:

student:
    name: student3
    age: 555
spring:
  rabbitmq:
    host: 192.168.75.130
    port: 5672
    username: guest
    password: guest

客户端依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.2.RELEASE</version>
    <relativePath/>
  </parent>
  <groupId>proj.ms</groupId>
  <artifactId>user-center</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>user-center</name>
  <description>user center</description>

  <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Greenwich.SR2</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-config</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-starter-bus-amqp</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>


    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </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>

</project>

  • 注:客户端需引入spring-cloud-starter-bus-amqp依赖,以便在配置发生变更的时候,通过消息队列获取配置消息。

添加注解

@SpringBootApplication
@EnableDiscoveryClient
public class UserCenterApplication {

    public static void main(String[] args) {

        SpringApplication.run(UserCenterApplication.class, args);
    }

}

添加配置

在resources目录中创建boostrap.yml文件,添加以下内容:

spring:
  application:
    name: user-service
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: dev
eureka:
  client:
    service-url:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/,http://peer3:8763/eureka/
server:
  port: 18080
management:
  endpoints:
    web:
      exposure:
        include: "*"
  • spring.cloud.config.discovery 中service-id指定配置将从名为config的微服务获取。

功能验证

启动user-service微服务,并修改Git仓库中的配置。观察消息队列、配置中心与user-service微服务日志消息队列日志是否正常。
RabbitMQ中消息存在消息生布的消费:

客户端日志,客户端已经重新从配置中心获取的新的配置:

2019-12-24 15:56:32.598  INFO 15408 --- [xuKlDf_RcOW8Q-1] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888/
2019-12-24 15:56:34.263  INFO 15408 --- [xuKlDf_RcOW8Q-1] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=user-service, profiles=[dev], label=null, version=7ea75c74cc03d422b00403a2b6fbc920ce487d87, state=null
2019-12-24 15:56:34.264  INFO 15408 --- [xuKlDf_RcOW8Q-1] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='configService', propertySources=[MapPropertySource {name='configClient'}, MapPropertySource {name='http://gitlab.figring.com/ms/config.git/user-service-dev.yml'}]}

配置动态刷新测试:

@RestController
@RefreshScope
public class UserController {

    @Value("${student.name}")
    private String name;

    @GetMapping("name")
    public String getName() {

        return name;
    }

}

动态修改配置,访问http://localhost:18080/name ,查看配置是否动态变化。

Spring Cloud Config高可用方案

架构图


Spring Cloud Config Server成为一个普通的微服务,在负载均衡组件的作用下为其它业务相关的微服务提供配置服务。

问题汇总

GitLab调用/actuator/bus-refresh端点时发生Json异常

原因及解决方案见上文。

 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!