Gateway Weight(权重)路由断言工厂

Weight 路由断言工厂需要两个参数:组(group)和权重(int 类型),注意,权重按组计算。语法如下:

Weight=组,权重

例如,为 https://weighthigh.org 地址创建两个路由,都放在 group1 分组下,如下:

spring:
 cloud:
   gateway:
     routes:
     - id: weight_high
       uri: https://weighthigh.org
       predicates:
       - Weight=group1, 8
     - id: weight_low
       uri: https://weightlow.org
       predicates:
       - Weight=group1, 2

该路由将把 ~80% 的流量转发到 weighthigh.org,把 ~20% 的流量转发到 weighlow.org。

示例

准备工作

为了验证权重断言,我们需要启动两个订单服务,如下图:

Gateway Weight(权重)路由断言工厂

上图中,两个订单服务的端口分别为 8081 和 18081。那么,如何在 IDEA 中启动两个订单服务呢?首先,修改一下 bootstrap.yml 配置文件,支持端口通过 JVM 参数指定,如下:

server:
  port: ${port:8081}

然后,右键“OrderApplication”选择“Copy Configuration...”复制一个运行配置,如下图:

Gateway Weight(权重)路由断言工厂

然后,再次右键新复制的配置,点击“Edit Configuration...”编辑运行配置,添加 VM 参数(端口)等信息,如下图:

Gateway Weight(权重)路由断言工厂

修改配置

将“Gateway 搭建网关服务”项目的配置文件 bootstrap.yml 内容使用如下配置替换:

server:
  # 网关端口
  port: 9000

spring:
  application:
    # 服务名称
    name: gateway-demo01
  cloud:
    # nacos
    nacos:
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
        group: DEFAULT_GROUP

    # 网关路由配置
    gateway:
      # 网关路由配置
      routes:
        # 路由id,自定义,只要唯一集合
        - id: service-order1
          # 路由的目标地址,lb 就是负载均衡,后面跟服务名,需要集成 nacos 等服务注册中心
          uri: http://localhost:18081
          # 路由断言,也就是判断请求是否符合路由规则的条件
          predicates:
            # Weight断言:根据权重转发请求
            - Weight=group1,2
        # 路由id,自定义,只要唯一集合
        - id: service-order2
          # 路由的目标地址,lb 就是负载均衡,后面跟服务名
          # 需要集成 nacos 等服务注册中心 lb://service-order
          uri: http://localhost:8081
          # 路由断言,也就是判断请求是否符合路由规则的条件
          predicates:
            # Weight断言:根据权重转发请求
            - Weight=group1,8

上面配置中配置了两个路由,分别是 service-order1 和 service-order2,其中 80% 的请求将发送给 service-order2,20% 的请求将发送给 service-order1。

此时,我们通过 Postman 工具向网关发送 10 次请求,如下图:

Gateway Weight(权重)路由断言工厂

Gateway Weight(权重)路由断言工厂

从上图可知,有两个请求发送到了 OrderApplication2 服务,有 8 个请求发送到 OrderApplication 服务。

源码分析

下面是 Weight 路由断言工厂源码:

package org.springframework.cloud.gateway.handler.predicate;

// ...

public class WeightRoutePredicateFactory extends AbstractRoutePredicateFactory<WeightConfig> implements ApplicationEventPublisherAware {

    private static final Log log = LogFactory.getLog(WeightRoutePredicateFactory.class);

    public static final String GROUP_KEY = WeightConfig.CONFIG_PREFIX + ".group";
    public static final String WEIGHT_KEY = WeightConfig.CONFIG_PREFIX + ".weight";

    private ApplicationEventPublisher publisher;

    public WeightRoutePredicateFactory() {
       super(WeightConfig.class);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
       this.publisher = publisher;
    }

    @Override
    public List<String> shortcutFieldOrder() {
       return Arrays.asList(GROUP_KEY, WEIGHT_KEY);
    }

    @Override
    public String shortcutFieldPrefix() {
          return WeightConfig.CONFIG_PREFIX;
    }

    @Override
    public void beforeApply(WeightConfig config) {
       if (publisher != null) {
          publisher.publishEvent(new WeightDefinedEvent(this, config));
       }
    }

    @Override
    public Predicate<ServerWebExchange> apply(WeightConfig config) {
       return exchange -> {
          Map<String, String> weights = exchange.getAttributeOrDefault(WEIGHT_ATTR,
                Collections.emptyMap());

          String routeId = exchange.getAttribute(GATEWAY_PREDICATE_ROUTE_ATTR);

          // 所有计算和与随机数的比较都在 WeightCalculatorWebFilter 中进行
          // all calculations and comparison against random num happened in
          // WeightCalculatorWebFilter
          String group = config.getGroup();
          if (weights.containsKey(group)) {

             String chosenRoute = weights.get(group);
             if (log.isTraceEnabled()) {
                log.trace("in group weight: "+ group + ", current route: " + routeId +", chosen route: " + chosenRoute);
             }

             return routeId.equals(chosenRoute);
          }

          return false;
       };
    }
}
说说我的看法
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号