欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Cloud 之 Config与动态路由.

程序员文章站 2022-11-08 14:08:04
一、简介  Spring Cloud Confg 是用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分。其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息、加密/解密信息等访问接口;而客户端则是微 ......

一、简介

 spring cloud confg 是用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分。其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息、加密/解密信息等访问接口;而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。

二、spring config server

搭建一个 config server,首先需要一个仓库,作为分布式配置中心的存储。这里我们选择了 github 作为我们的仓库:https://github.com/jmcuixy/cloud-config-server/tree/master/config-repo
Spring Cloud 之 Config与动态路由.

1. pom.xml

    <dependencies>
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-config-server</artifactid>
        </dependency>

        <!--启动 security 保护,不需要可不添加-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-security</artifactid>
        </dependency>

        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid>
        </dependency>
    </dependencies>

2. application.yml

server:
  port: 7001

spring:
  application:
    name: cloud-config-server

  # 配置完成后可访问的 url 如下,比如:http://localhost:7001/env/default
  # /{application}/{profile} [/{label}]
  # /{application}-{profile}.yml
  # /{label}/{application}-{profile}.yml
  # /{application}-{profile}.properties
  # /{label}/{application}-{profile}.properties
  cloud:
    config:
      # 为配置中心提供安全保护
      username: user
      password: password
      server:
        git:
          # 仓库地址
          uri: https://github.com/jmcuixy/cloud-config-server.git
          # 搜索路径
          search-paths: config-repo
        # 访问 http://localhost:7001/actuator/health 可以获取配置中心健康指标
        health:
          repositories:
            env:
              name: env
              profiles: default
              label: master
            env-dev:
              name: env-dev
              profiles: dev
              label: master
            env-test:
              name: env-test
              profiles: test
              label: master
            env-prod:
              name: env-prod
              profiles: prod
              label: master

  # 提供 security 保护
  security:
    user:
      name: user
      password: password

management:
  endpoint:
    health:
      enabled: true
      show-details: always

eureka:
  client:
    service-url:
      defaultzone: http://user:password@localhost:1111/eureka/

这里我没有配置 github 的 username 和 password,用的是 ssh key 的方式。

3. configapplication.java

// 开启 spring cloud config 的 server 功能
@enableconfigserver
@enablediscoveryclient
@springbootapplication
public class configapplication {

    public static void main(string[] args) {
        springapplication.run(configapplication.class, args);
    }

}

至此,一个 spring cloud config server 就搭建完成了。上面的配置中,我们将 config server 注册到 eureka server 中,当作整个系统服务的一部分,所以config client 只要利用 eureka 的服务发现维持与 config server 通信就可以了。

在config server 的文件系统中,每次客户端请求获取配置信息时,confg server 从 git 仓库中获取最新配置到本地,然后在本地 git 仓库中读取并返回。当远程仓库无法获取时,直接将本地内容返回。

二、spring config client

spring cloud confg 的客户端在启动的时候,默认会从工程的 classpath 中加载配置信息并启动应用。只有当我们配置 spring.cloud.config.uri(或者spring.cloud.config.discovery) 的时候,客户端应用才会尝试连接 spring cloud confg 的服务端来获取远程配置信息并初始化 spring 环境配置。同时,我们必须将该参数配置在bootstrap.yml、环境变量或是其他优先级高于应用 jar 包内的配置信息中,才能正确加载到远程配置。

1. pom.xml

    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>

        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-config-client</artifactid>
        </dependency>

        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid>
        </dependency>

        <!-- 当连接 config-server 失败的时候,可增加重试-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-aop</artifactid>
        </dependency>
        <dependency>
            <groupid>org.springframework.retry</groupid>
            <artifactid>spring-retry</artifactid>
        </dependency>

        <!--配置动态刷新-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-actuator</artifactid>
        </dependency>
    </dependencies>

2. bootstrap.yml 和 application.yml

  • bootstrap.yml
spring:
  application:
    # 对应配置文件规则中的 {application} 部分
    name: env
  cloud:
    config:
      name: env
      # uri: http://localhost:7001
      discovery:
        enabled: true
        service-id: cloud-config-server
      # 环境变量  
      profile: default
      # 分支
      label: master
      # config server 配置的安全信息
      username: user
      password: password
      # 快速失败响应(当发现 config-server 连接失败时,就不做连接的准备工作,直接返回失败)
      fail-fast: true
      # 失败重试
      retry:
        # 初始重试间隔时间,毫秒
        initial-interval: 1000
        # 下一间隔的乘数
        multiplier: 1.1
        # 最大间隔时间
        max-interval: 2000
        # 最多重试次数
        max-attempts: 6

bootstrap 配置会系统会优先加载,加载优先级比 application 高。

  • application.yml
server:
  port: 7002

spring:
  application:
    name: cloud-config-client

eureka:
  client:
    service-url:
      defaultzone: http://user:password@localhost:1111/eureka/

management:
  endpoints:
    web:
      exposure:
        # 开启指定端点
        # 配置刷新地址:post http://127.0.0.1:7002/actuator/refresh
        include: 'refresh'

3. configclientapplication.java

@enablediscoveryclient
@springbootapplication
public class configclientapplication {

    public static void main(string[] args) {
        springapplication.run(configclientapplication.class, args);
    }

}

4. 应用

接下来瞅瞅客户端要怎么读到服务器的配置项呢?

@refreshscope
@restcontroller
public class configclientadmin {

    @value("${from:default}")
    private string from;

    @autowired
    private environment environment;


    @requestmapping("/from")
    public string from() {
        string fromenv = environment.getproperty("from");
        return from + "_" + fromenv;
    }
}

如上,我们可以使用 @value 注解注入配置信息,或者使用 environment bean 来获取配置项。

需要注意的是,当服务端的配置项更新的时候,客户端并不会同步获得更新,需要 post 方法执行 "/actuator/refresh" 来刷新配置项。

@refreshscope 注解使配置的内容动态化,当使用 http://127.0.0.1:7002/actuator/refresh 刷新配置的时候,会刷新带有 @refreshscope 的 bean。

三、动态路由

我们尝试用 spring cloud zuul 搭建了网关服务,但是我们发现路由信息都配置在 application.yml 中,这对网关的高可用是个不小的打击,因为网关作为系统流量的路口,总不能因为改个路由信息天天重启网关吧?所以动态路由的实现,就变得迫不及待了,好在我们现在有了 spring cloud config。

首先,我们将 spring cloud zuul 的路由信息,配置在 config server 的 env.yml 中:

zuul:
  routes:
    client-1:
      # ?:匹配任意单个数量字符;*:匹配任意多个数量字符;**:匹配任意多个数量字符,支持多级目录
      # 使用 url 的配置没有线程隔离和断路器的自我保护功能,不推荐使用
      path: /client-1/**
      url: http://localhost:2222/
      # 敏感头信息设置为空,表示不过滤敏感头信息,允许敏感头信息渗透到下游服务器
      sensitiveheaders: ""
      customsensitiveheaders: true
    client-2:
      path: /client-2/**
      serviceid: cloud-eureka-client
    # zuul.routes.<serviceid> = <path>
    cloud-eureka-client: /client-3/**
    client-4:
      path: /client-4/**
      # 请求转发 —— 仅限转发到本地接口
      url: forward:/local

  # zuul 将对所有的服务都不自动创建路由规则
  ignored-services: "*"
  # 对某些 url 设置不经过路由选择
  ignored-patterns: {"/**/world/**","/**/zuul/**"}
  # spring cloud zuul在请求路由时,会过滤掉 http 请求头(cookie、set-cookie、authorization)信息中的一些敏感信息,
  sensitive-headers: {"cookie", "set-cookie", "authorization"}
  # 网关在进行路由转发时为请求设置 host 头信息(保持在路由转发过程中 host 头信息不变)
  add-host-header: true
  # 请求转发时加上 x-forwarded-*头域
  add-proxy-headers: true
  # 是否开启重试,默认关闭
  retryable: true
  # 通过 /zuul 路径访问的请求会绕过 dispatcherservlet, 被 zuu1servlet 处理,主要用来应对处理大文件上传的情况。
  servlet-path: /zuul
  # 禁用某个过滤器 zuul.<simpleclassname>.<filtertye>.disable=true
  tokenfilter:
    pre:
      disable: true

然后,我们将网关服务注册为 config client(配置项与上面类似,就不赘述了),从 config server 获取路由信息:

@enablezuulproxy
@enablediscoveryclient
@springbootapplication
public class dynamicrouteapplication {

    public static void main(string[] args) {
        springapplication.run(dynamicrouteapplication.class, args);
    }

    /**
     * 刷新地址:post http://127.0.0.1:5006/actuator/refresh
     * 路由查看地址:get http://127.0.0.1:5006/actuator/routes
     *
     * @return
     */
    @bean
    @primary
    //该注解来使 zuul 的配置内容动态化
    @refreshscope
    @configurationproperties(prefix = "zuul")
    public zuulproperties zuulproperties() {
        return new zuulproperties();
    }

}

这样,就把我们的路由信息交给 config server 去管理了~~

演示源代码 :https://github.com/jmcuixy/spring-cloud-demo

内容参考:《spring cloud 微服务实战》