Spring Cloud 之服务网关 Zuul (三) 灰度发布

Spring Cloud 之服务网关 Zuul (三) 灰度发布

概述

常见的发布方式有灰度发布、蓝绿发布、金丝雀发布及 AB 发布等. 所谓灰度发布是指, 我们要发布版本了, 在不确定正确性的情况下, 我们选择先部分节点升级, 然后让一些特定的流量进入到这些新节点,完成测试后再全量发布. 灰度发布有多种方式, 本文主要介绍基于 Eureka 的元数据(metadata)的方式实现

元数据(medadata)

Eureka里面主要是有两种元数据:

  • 标准元数据: 服务的各种注册信息
  • 自定义元数据: 自定义数据信息, 通过 eureka.instance.metadata-map.= 来实现. 会保存到 Eureka 的注册表中, 但对微服务生态的任何行为都没有任何影响

Java 代码

整体架构

  • 一个 eureka 服务
  • 一个 zuul 网关服务
  • 一个 应用服务启动两个实例

编写 Eureka 服务

参考: Spring Cloud 之 Eureka 服务注册与发现

编写 Zuul 网关服务

  • 编写 pom 文件

    在 zuul-server 里引入一个开源项目包: github地址

    <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-netflix-zuul</artifactId>
    </dependency>
    <!-- 灰度发布 https://github.com/jmnarloch/ribbon-discovery-filter-spring-cloud-starter-->
    <dependency>
        <groupId>io.jmnarloch</groupId>
        <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
        <version>2.1.0</version>
    </dependency>
    
  • 定义负载均衡过滤器

    import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.*;
    @Configuration
    public class GrayFilter extends ZuulFilter {
          
          
        @Override
        public boolean shouldFilter() {
          
          
            RequestContext ctx = RequestContext.getCurrentContext();
            return !ctx.containsKey(FORWARD_TO_KEY)
                    && !ctx.containsKey(SERVICE_ID_KEY);
        }
        @Override
        public Object run()  {
          
          
            HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
            // 所有的请求在 header 里放入 gray_mark 标识
            String mark = request.getHeader("gray_mark");
            if(StringUtils.isNotEmpty(mark)&& "enable".equals(mark)){
          
          
                // 当灰度发布功能开启的时候, 该请求被路由到 host-mark 值为 gray-host 这台服务器上
                RibbonFilterContextHolder.getCurrentContext().add("host-mark","gray-host");
            }else{
          
          
                // 其他正常请求被路由到其他正常运行的服务器上
                RibbonFilterContextHolder.getCurrentContext().add("host-mark","running-host");
            }
            return null;
        }
        @Override
        public String filterType() {
          
          
            return PRE_TYPE;
        }
        @Override
        public int filterOrder() {
          
          
            return PRE_DECORATION_FILTER_ORDER -1;
        }
    }
    

编写应用程序服务

  • 编写 pom 文件

    <dependencies>
        <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-web</artifactId>
        </dependency>
    </dependencies>
    
  • 编写工程的配置文件, 注意 eureka.instance.metadata-map.host-mark=running-host 配置. 该应用程序启动两个实例, 一个正常启动为服务器A, 一个通过命令行启动: java -Dserver.port=8764 -Deureka.instance.metadata-map.host-mark=gray-host -jar joke-server-1.0.0-SNAPSHOT.jar, 为服务器B

    eureka:
      client:
        serverUrl:
          defaultZone: http://localhost:8761/eureka/
      instance:
        lease-renewal-interval-in-seconds: 5
        lease-expiration-duration-in-seconds: 15
        perfer-in-address: true
        metadata-map:
          host-mark: running-host
    
    server:
      port: 8763
    
    spring:
      application:
        name: joke-server
    
  • 编写控制器

    @RestController
    @RequestMapping("/jokes")
    @Slf4j
    public class JokeController {
          
          
        @GetMapping("/list")
        public String getJokes() {
          
          
            log.debug("Get jokes");
            return "jokes";
        }
    
    }
    

发送请求验证结果

发送请求通过 zuul 网关路由到应用服务器, 当请求的 header 里的 gray_mark 标识的值不为 enable 的时候, 请求被分配到服务器 A 上, 当 gray_mark 标识的值为 enable 的时候, 请求被分配到服务器 B 上.

  • 发送请求: http://localhost:5555/joke-server/jokes/list

    header:

    headers = {
          
          
      'gray_mark': 'unable',
    }
    

    请求被路由到服务A上

  • 发送请求: http://localhost:5555/joke-server/jokes/list

    header:

    headers = {
          
          
      'gray_mark': 'enable',
    }
    

    请求被路由到服务B上

猜你喜欢

转载自blog.csdn.net/zjhcxdj/article/details/106992956