6.1 分布式配置中⼼应⽤场景

往往,我们使⽤配置⽂件管理⼀些配置信息,⽐如application.yml
单体应⽤架构,配置信息的管理、维护并不会显得特别麻烦,⼿动操作就可以,因为就⼀个⼯程;
微服务架构,因为我们的分布式集群环境中可能有很多个微服务,我们不可能⼀个⼀个去修改配置然后重启⽣效,在⼀定场景下我们还需要在运⾏期间动态调整配置信息,⽐如:根据各个微服务的负载情况,动态调整数据源连接池⼤⼩,我们希望配置内容发⽣变化的时候,微服务可以⾃动更新。
场景总结如下:
1)集中配置管理,⼀个微服务架构中可能有成百上千个微服务,所以集中配置管理是很重要的(⼀次修改、到处⽣效)
2)不同环境不同配置,⽐如数据源配置在不同环境(开发dev,测试test,⽣产prod)中是不同的
3)运⾏期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池⼤⼩等配置修改后可⾃动更新
4)如配置内容发⽣变化,微服务可以⾃动更新配置
那么,我们就需要对配置⽂件进⾏集中式管理,这也是分布式配置中⼼的作⽤。

6.2 Spring Cloud Config

6.2.1 Config简介

Spring Cloud Config是⼀个分布式配置管理⽅案,包含了 Server端和 Client端两个部分。
image.png
image.png

  • Server 端:提供配置⽂件的存储、以接⼝的形式将配置⽂件的内容提供出去,通过使⽤@EnableConfifigServer注解在 Spring boot 应⽤中⾮常简单的嵌⼊
  • Client 端:通过接⼝获取配置数据并初始化⾃⼰的应⽤

    6.2.2 Config分布式配置应⽤

    说明:Config Server是集中式的配置服务,⽤于集中管理应⽤程序各个环境下的配置。 默认使⽤Git存储配置⽂件内容,也可以SVN。
    ⽐如,我们要对“简历微服务”的application.yml进⾏管理(区分开发环境、测试环境、⽣产环境)
    1)登录码云,创建项⽬lagou-confifig-repo
    2)上传yml配置⽂件,命名规则如下:
    {application}-{profifile}.yml 或者 {application}-{profile}.properties
    其中,application为应⽤名称,profifile指的是环境(⽤于区分开发环境,测试环境、⽣产环境等)
    示例:lagou-service-resume-dev.yml、lagou-service-resume-test.yml、lagou-service-resume-prod.yml
    3)构建Config Server统⼀配置中⼼
    新建SpringBoot⼯程,引⼊依赖坐标(需要注册⾃⼰到Eureka) ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0

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

      <artifactId>lagou-parent</artifactId>
      <groupId>com.lagou.edu</groupId>
      <version>1.0-SNAPSHOT</version>
    

    4.0.0 lagou-cloud-configserver-9006
<dependencies>
    <!--eureka client 客户端依赖引入-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--config配置中心服务端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>
</dependencies>

配置启动类,使⽤注解@EnableConfifigServer开启配置中⼼服务器功能
```java
package com.lagou.edu;

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


@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer  // 开启配置中心功能
public class ConfigServerApplication9006 {

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

application.yml配置

server:
  port: 9006
#注册到Eureka服务中心
eureka:
  client:
    service-url:
      # 注册到集群,就把多个Eurekaserver地址使用逗号连接起来即可;注册到单实例(非集群模式),那就写一个就ok
      defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
  instance:
    prefer-ip-address: true  #服务实例中显示ip,而不是显示主机名(兼容老的eureka版本)
    # 实例名称: 192.168.1.103:lagou-service-resume:8080,我们可以自定义它
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
spring:
  application:
    name: lagou-cloud-configserver
  cloud:
    config:
      server:
        git:
          uri: https://github.com/5173098004/lagou-config-repo.git #配置git服务地址
          username: 517309804@qq.com #配置git用户名
          password: yingdian12341 #配置git密码
          search-paths:
            - lagou-config-repo
      # 读取分支
      label: master
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
#针对的被调用方微服务名称,不加就是全局生效
#lagou-service-resume:
#  ribbon:
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #负载策略调整
# springboot中暴露健康检查等断点接口
management:
  endpoints:
    web:
      exposure:
        include: "*"
  # 暴露健康接口的细节
  endpoint:
    health:
      show-details: always

测试访问:http://localhost:9006/master/lagou-service-resume-dev.yml,查看到配置⽂件内容
4)构建Client客户端(在已有简历微服务基础上)
已有⼯程中添加依赖坐标


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

application.yml修改为bootstrap.yml配置⽂件
bootstrap.yml是系统级别的,优先级⽐application.yml⾼,应⽤启动时会检查这个配置⽂件,在这个配置⽂件中指定配置中⼼的服务地址,会⾃动拉取所有应⽤配置并且启⽤。(主要是把与统⼀配置中⼼连接的配置信息放到bootstrap.yml)
注意:需要统⼀读取的配置信息,从集中配置中⼼获取
bootstrap.yml

server:
  port: 8080
spring:
  application:
    name: lagou-service-resume
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/lagou?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
  jpa:
    database: MySQL
    show-sql: true
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl  #避免将驼峰命名转换为下划线命名
  cloud:
    # config客户端配置,和ConfigServer通信,并告知ConfigServer希望获取的配置信息在哪个文件中
    config:
      name: lagou-service-resume  #配置文件名称
      profile: dev  #后缀名称
      label: master #分支名称
      uri: http://localhost:9006    #ConfigServer配置中心地址
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
#注册到Eureka服务中心
eureka:
  client:
    service-url:
      # 注册到集群,就把多个Eurekaserver地址使用逗号连接起来即可;注册到单实例(非集群模式),那就写一个就ok
      defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
  instance:
    prefer-ip-address: true  #服务实例中显示ip,而不是显示主机名(兼容老的eureka版本)
    # 实例名称: 192.168.1.103:lagou-service-resume:8080,我们可以自定义它
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
    # 自定义Eureka元数据
    metadata-map:
      cluster: cl1
      region: rn1

6.3 Config配置⼿动刷新

不⽤重启微服务,只需要⼿动的做⼀些其他的操作(访问⼀个地址/refresh)刷新,之后再访问即可此时,客户端取到了配置中⼼的值,但当我们修改GitHub上⾯的值时,服务端(Config Server)能实时获取最新的值,但客户端(Config Client)读的是缓存,⽆法实时获取最新值。Spring Cloud已经为我们解决了这个问题,那就是客户端使⽤post去触发refresh,获取最新数据。
1)Client客户端添加依赖springboot-starter-actuator(已添加)
2)Client客户端bootstrap.yml中添加配置(暴露通信端点)

management:
  endpoints:
    web:
      exposure:
        include: "*"

3)Client客户端使⽤到配置信息的类上添加@RefreshScope
4)⼿动向Client客户端发起POST请求,http://localhost:8080/actuator/refresh,刷新配置信息
注意:⼿动刷新⽅式避免了服务重启(流程:Git改配置—>for循环脚本⼿动刷新每个微服务)
思考:可否使⽤⼴播机制,⼀次通知,处处⽣效,⽅便⼤范围配置刷新?

6.4 Config配置⾃动更新

实现⼀次通知处处⽣效
拉勾内部做分布式配置,⽤的是zk(存储+通知),zk中数据变更,可以通知各个监听的客户端,客户端收到通知之后可以做出相应的操作(内存级别的数据直接⽣效,对于数据库连接信息、连接池等信息变化更新的,那么会在通知逻辑中进⾏处理,⽐如重新初始化连接池)
在微服务架构中,我们可以结合消息总线(Bus)实现分布式配置的⾃动更新(Spring Cloud Config+Spring Cloud Bus)

6.4.1 消息总线Bus

所谓消息总线Bus,即我们经常会使⽤MQ消息代理构建⼀个共⽤的Topic,通过这个Topic连接各个微服务实例,MQ⼴播的消息会被所有在注册中⼼的微服务实例监听和消费。换⾔之就是通过⼀个主题连接各个微服务,打通脉络。
Spring Cloud Bus(基于MQ的,⽀持RabbitMq/Kafka) 是Spring Cloud中的消息总线⽅案,Spring Cloud Config + Spring Cloud Bus 结合可以实现配置信息的⾃动更新。

6.4.2 Spring Cloud Config+Spring Cloud Bus 实现⾃动刷新

MQ消息代理,我们还选择使⽤RabbitMQ,ConfifigServer和ConfifigClient都添加都消息总线的⽀持以及与RabbitMq的连接信息
1)Config Server服务端添加消息总线⽀持

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

2)ConfigServer添加配置

spring:
 rabbitmq:
 host: 127.0.0.1
 port: 5672
 username: guest
 password: guest

3)微服务暴露端⼝

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

建议暴露所有的端⼝
management:
 endpoints:
 web:
 exposure:
 include: "*"

5)重启各个服务,更改配置之后,向配置中⼼服务端发送post请求
http://localhost:9003/actuator/bus-refresh,各个客户端配置即可⾃动刷新
在⼴播模式下实现了⼀次请求,处处更新,如果我只想定向更新呢?
在发起刷新请求的时候http://localhost:9006/actuator/bus-refresh/lagou-service-resume:8081
即为最后⾯跟上要定向刷新的实例的 服务名:端⼝号即可