SpringCloud高级应用-Gateway

news2024/11/24 22:39:25

Dubbo SpringCloud高级应用

    • 1. SpringCloud技术栈
      • 1.1 SpringCloud技术栈
      • 1.2 SpringCloud经典技术介绍
      • 1.3 SpringCloud项目场景
    • 2. SpringCloud Gateway
      • 2.1 Gateway工作原理
      • 2.2 Gateway路由
        • 2.2.1 业务说明
        • 2.2.2 基于配置路由设置
        • 2.2.3 基于代码路由配置
        • 2.2.4 Gateway-Predicate
        • 2.2.5 断言源码剖析
        • 2.2.5 自定义断言
      • 2.3 Gateway过滤器
        • 2.3.1 过滤器分类
        • 2.3.2 默认过滤器的使用
        • 2.3.3自定义GatewayFilter
        • 2.3.4 自定义GlobalFilter
      • 2.4 跨域配置
        • 2.4.1 yml方式
        • 2.4.1 @Bean配置
      • 2.5 限流
        • 2.5.1 令牌桶算法讲解
        • 2.5.2 限流案例

1. SpringCloud技术栈

开发分布式系统复杂性已从应用程序层转移到网络层,并要求服务之间进行更多的交互。将代码设为“cloud-native”就需要解决12-factor,例如外部配置,服务无状态,日志记录以及连接到备份服务之类的问题,Spring Cloud项目套件包含使您的应用程序在云中运行所需的许多服务
12-factor(云原生应用程序的12要素):
在这里插入图片描述
SpringCloud架构:
在这里插入图片描述

1.1 SpringCloud技术栈

在这里插入图片描述

1.2 SpringCloud经典技术介绍

淘汰的技术组件的替换方案

Eureka闭源:
在这里插入图片描述
上面英文大概意思是:Eureka 2.0 的开源工作已经停止,依赖于开源库里面的 Eureka 2.x 分支构建的项目或者相关代码,风险自负。
Eureka在微服务项目中主要承担服务注册与发现工作,可以替代的技术方案比较多,而且很多方案都比Eureka优秀,比如Consul、Nacos等

Hystrix停止更新:
在这里插入图片描述
Hystrix在项目中主要做服务熔断、降级,但官方宣布将不在开发,目前处于维护状态,但官方表示 1.5.18 版本的 Hystrix 已经足够稳定,可以满足 Netflix 现有应用的需求。
关于Hystrix可替代的产品也比较多,比如官方推荐的resilience4j,resilience4j是一个轻量级熔断框架,但resilience4j目前在国内使用频率还不高,功能也不够强,我们更推荐使用功能更加强悍的SpringCloud Alibaba Sentinel

Zuul过时:
Zuul是一个微服务网关技术,但Zuul1.x使用的是阻塞式的API,不支持长连接,没有提供异步,高并发场景下性能低。SpringCloud官网推出了全新的微服务网关技术SpringCloud Gateway,比Zuul性能更强悍、功能更丰富、且支持异步等多种特性。

SpringCloud Config实用性差:
SpringCloud Config主要用于管理项目的配置文件,每次要使用SpringCloud Config的时候,总得经过一波操作和配置的折腾,才可以使用SpringCloud Config实现配置管理,而且单独使用无法支持配置实时刷新,在项目中用起来,真比较头疼的。
当前有很多技术可以取代SpringCloud Config,比如携程的Apollo、SpringCloud Alibaba Nacos,功能都比SpringCloud Config强,并且支持实时刷新配置。

SpringCloud Bus实用性差:
SpringCloud Bus是服务消息总线,主要实现通知多个服务执行某个任务,一般和SpringCloud Config一起使用。这个功能其实不太使用,因为很多任务组件基本都具备消息通知功能,比如Nacos、Apollo都能实现所有服务订阅执行相关操作。

1.3 SpringCloud项目场景

在这里插入图片描述
微服务技术目前已经在很多国内外大厂中都在广泛使用,以滴滴快车业务未来,来讲解一下微服务技术结合业务应用讲解一下
在这里插入图片描述
打车业务如上图:

1:打车的时候会选择车型,选择车型我们调用过程是:Gateway->Driver(加载司机列表)
2:选择车型后确认打车,相当于要下单了,调用过程是:Gateway->Order(下单)->Driver(司机状态更改)
3:打车结束后,用户进入支付,调用过程是:Gateway->Pay(支付)->Driver(更新司机状态)->Order(更新订单状态)

2. SpringCloud Gateway

在这里插入图片描述
Spring Cloud Gateway 是Spring Cloud团队的一个全新项目,基于Spring 5.0、SpringBoot2.0、Project Reactor 等技术开发的网关。旨在为微服务架构提供一种简单有效统一的API路由管理方式。
Spring Cloud Gateway 作为SpringCloud生态系统中的网关,目标是替代Netflix Zuul。Gateway不仅提供统一路由方式,并且基于Filter链的方式提供网关的基本功能。例如:安全,监控/指标,和限流。
总结:微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控,限流等相关功能。
Gateway官方网址>

2.1 Gateway工作原理

Gateway工作流程图如下:
在这里插入图片描述

  1. Gateway的客户端回向Spring Cloud Gateway发起请求,请求首先会被HttpWebHandlerAdapter进行提取组装成网关的上下文,然后网关的上下文会传递到DispatcherHandler。
  2. DispatcherHandler是所有请求的分发处理器,DispatcherHandler主要负责分发请求对应的处理器,比如将请求分发到对应RoutePredicateHandlerMapping(路由断言处理器映射器)。
  3. 路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilteringWebHandler。
  4. FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理,处理完毕后,将Response返回到Gateway客户端。
    在Filter链中,通过虚线分割Filter的原因是,过滤器可以在转发请求之前处理或者接收到被代理服务的返回结果之后处理。所有的Pre类型的Filter执行完毕之后,才会转发请求到被代理的服务处理。被代理的服务把所有请求完毕之后,才会执行Post类型的过滤器。

2.2 Gateway路由

Gateway路由配置分为基于配置的静态路由设置和基于代码动态路由配置
静态路由是指在application.yml中把路由信息配置好了,而动态路由则支持在代码中动态加载路由信息,更加灵活

2.2.1 业务说明

在这里插入图片描述

1:用户所有请求以/order开始的请求,都路由到 hailtaxi-order服务
2:用户所有请求以/driver开始的请求,都路由到 hailtaxi-driver服务
3:用户所有请求以/pay开始的请求,都路由到 hailtaxi-pay服务

2.2.2 基于配置路由设置

在这里插入图片描述

如上图所示,正是Gateway静态路由配置:
1:用户所有请求以/order开始的请求,都路由到 hailtaxi-order服务
2:用户所有请求以/driver开始的请求,都路由到 hailtaxi-driver服务
3:用户所有请求以/pay开始的请求,都路由到 hailtaxi-pay服务

id:唯一标识符
uri:路由地址,可以是 lb://IP:端口     也可以是   lb://${spring.application.name}
predicates:断言,是指路由条件
- Path=/driver/**:路由条件。Predicate 接受一个输入参数,返回一个布尔值结果。这里表示匹配所有以driver开始的请求。
filters:过滤器
- StripPrefix=1:真实路由的时候,去掉第1个路径,路径个数以/分割区分

2.2.3 基于代码路由配置

所有路由规则我们可以从数据库中读取并加载到程序中。基于代码的路由配置我们只需要创建RouteLocator并添加路由配置即可,代码如下:

/***
 * 路由配置
 * @param builder
 * @return
 */
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {

    return builder.routes()
        .route("hailtaxi-driver", r -> r.path("/driver/**").uri("lb://hailtaxi-driver"))
        .route("hailtaxi-order", r -> r.path("/order/**").uri("lb://hailtaxi-order"))
        .route("hailtaxi-pay", r -> r.path("/pay/**").uri("lb://hailtaxi-pay"))
        .build();
}

在真实场景中,基于配置文件的方式更直观、简介,但代码的路由配置是更强大,可以实现很丰富的功能,可以把路由规则存在数据库中,每次直接从数据库中加载规则,这样的好处是可以动态刷新路由规则,通常应用于权限系统动态配置。

spring: 
    cloud: 
        gateway:
          #路由配置
          routes:
            #唯一标识符
            - id: hailtaxi-driver
              uri: lb://hailtaxi-driver
              #路由断言
              predicates:
                - Path=/driver/**
            #唯一标识符
            - id: hailtaxi-order
              uri: lb://hailtaxi-order
              #路由断言
              predicates:
                - Path=/order/**
            #唯一标识符
            - id: hailtaxi-pay
              uri: lb://hailtaxi-pay
              #路由断言
              predicates:
                - Path=/pay/**

2.2.4 Gateway-Predicate

上面路由匹配规则中我们都用了- Path方式,其实就是路径匹配方式,除了路径匹配方式,Gateway还支持很多丰富的匹配方式
关于Predicate学习地址,可以参考官网或者链接2
routes下面的属性含义如下:

id:我们自定义的路由 ID,保持唯一
uri:目标服务地址
predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该属性包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,通过 Header、请求参数等不同的条件来作为条件匹配到对应的路由
Spring Cloud 内置的几种 Predicate 的实现:
在这里插入图片描述

2.2.5 断言源码剖析

已Cookie断言为例,首先看它的体系结构
在这里插入图片描述

public class CookieRoutePredicateFactory
        extends AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Config> {

    /**
     * Name key.
     */
    public static final String NAME_KEY = "name";

    /**
     * Regexp key.
     */
    public static final String REGEXP_KEY = "regexp";

    public CookieRoutePredicateFactory() {
        super(Config.class);
    }
    
    /*
      通过shortcutFieldOrder方法设置Config配置类中的属性,需要根据具体的规则来设置
      通过shortcutType方法获取具体规则,具体参看:org.springframework.cloud.gateway.support.ShortcutConfigurable.ShortcutType
      规则包括以下几种:
        DEFAULT : 按照shortcutFieldOrder顺序依次赋值
    */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(NAME_KEY, REGEXP_KEY);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                List<HttpCookie> cookies = exchange.getRequest().getCookies()
                        .get(config.name);
                if (cookies == null) {
                    return false;
                }
                for (HttpCookie cookie : cookies) {
                    if (cookie.getValue().matches(config.regexp)) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public String toString() {
                return String.format("Cookie: name=%s regexp=%s", config.name,
                        config.regexp);
            }
        };
    }
    
    /*
     内部配置类是用来接收在配置文件中配置的参数的
      routes:
        #唯一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Cookie=username,itheima
    */
    @Validated
    public static class Config {

        @NotEmpty
        private String name;

        @NotEmpty
        private String regexp;

        public String getName() {
            return name;
        }

        public Config setName(String name) {
            this.name = name;
            return this;
        }

        public String getRegexp() {
            return regexp;
        }

        public Config setRegexp(String regexp) {
            this.regexp = regexp;
            return this;
        }

    }

}

2.2.5 自定义断言

尽管Spring Cloud Gateway已经包含了很多路由匹配规则,有时候我们需要开发自定义路由匹配规则来满足需求,下面简单的介绍一下如何自定义路由匹配规则
需求:转发带token的请求到hailtaxi-drvier服务中,这里定义请求带token是指包含某个请求头的请求,至于是什么请求头可以由配置指定

  1. 修改配置文件
    gateway:
      #路由配置
      routes:
        #唯一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            # 自定义一个Token断言,如果请求包含Authorization的token信息则通过
            - Token=Authorization
  1. 创建 RoutePredicateFactory
    断言工厂默认命名规则必须按照"名称"+RoutePredicateFactory,如上TokenRoutePredicateFactory的断言名称为Token
@Slf4j
@Component // 要交给spring容器管理
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenRoutePredicateFactory.Config> {

    public TokenRoutePredicateFactory() {
        super(Config.class);
    }

    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            // 打印配置文件参数值
            String headerName = config.getHeaderName();
            HttpHeaders headers = exchange.getRequest().getHeaders();
            List<String> header = headers.get(headerName);
            log.info("Token Predicate headers:{}", header);
            // 断言返回的是boolean值
            return header!=null && header.size()>0;
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("headerName");//指定配置文件中加载到的配置信息应填充到Config的哪个属性上
    }

    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.DEFAULT;
    }

    @Data
    public static class Config { //static class
        private String headerName;//存储从配置文件中加载的配置
    }
}

2.3 Gateway过滤器

Spring Cloud Gateway根据作用范围划分为GatewayFilterGlobalFilter,二者区别如下:
GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.gateway.default-filters配置在全局,作用在所有路由上;gateway内置了多种过滤器工厂,配套的过滤器可以直接使用,如下图所示:
在这里插入图片描述
在这里插入图片描述
GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
在这里插入图片描述
过滤器作为Gateway的重要功能。常用于请求鉴权、服务调用时长统计、修改请求或响应header、限流、去除路径等等。
关于Gateway过滤器的更多使用,可以参考官网

2.3.1 过滤器分类

默认过滤器:出厂自带,实现好了拿来就用,不需要实现
  全局默认过滤器
  局部默认过滤器
  
自定义过滤器:根据需求自己实现,实现后需配置,然后才能用哦。
  全局过滤器:作用在所有路由上。
  局部过滤器:配置在具体路由下,只作用在当前路由上。

默认过滤器十好几个,常见如下:

过滤器名称说明对应的类父类
AddRequestHeader对匹配上的请求加上HeaderAddRequestHeaderGatewayFilterFactoryAbstractNameValueGatewayFilterFactory
AddRequestParameters对匹配上的请求路由AddRequestHeaderGatewayFilterFactoryAbstractNameValueGatewayFilterFactory
AddResponseHeader对从网关返回的响应添加HeaderAddResponseHeaderGatewayFilterFactoryAbstractNameValueGatewayFilterFactory
StripPrefix对匹配上的请求路径去除前缀StripPrefixGatewayFilterFactoryAbstractGatewayFilterFactory

2.3.2 默认过滤器的使用

  • 添加响应头
spring:
  cloud:
    gateway:
     # 配置全局默认过滤器 作用在所有路由上,也可单独为某个路由配置
      default-filters:
      # 往响应过滤器中加入信息
        - AddResponseHeader=X-Response-Default-MyName,itheima
  • 前缀处理
    在项目中做开发对接接口的时候,我们很多时候需要统一API路径,比如统一以/api开始的请求调用hailtaxi-driver服务,但真实服务接口地址又没有/api路径,我们可以使用Gateway的过滤器处理请求路径
    在gateway中可以通过配置路由的过滤器StripPrefix实现映射路径中的前缀处理
    gateway:
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/api/driver/**
        filters:
          - StripPrefix=1

当请求路径匹配到/hello/world/HiController/**会将包含hello和后边的字符串接去掉转发, StripPrefix=2就代表截取路径的个数,这样配置后当请求/hello/world/HiController/aaa后端匹配到的请求路径,就会变成http://localhost:8762/HiController/aaa

    gateway:
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/**
        filters:
          - PrefixPath=/driver

作用同上, 只是去掉固定路径

2.3.3自定义GatewayFilter

  1. 实现GatewayFilter接口
    GatewayFilter 一般作用在某一个路由上,需要实例化创建才能使用,局部过滤器需要实现接口GatewayFilter、Ordered
public class PayFilter implements GatewayFilter,Ordered {

    /***
     * 过滤器执行拦截
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("GatewayFilter拦截器执行---pre-----PayFilter");
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            System.out.println("GatewayFilter拦截器执行---post-----PayFilter");
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

使用局部过滤器:

/***
 * 路由配置
 * @param builder
 * @return
 */
 @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("hailtaxi-driver", r -> r.path("/api/driver/**")
                        .uri("lb://hailtaxi-driver")
                        .filter(new PayFilter())
                 )
                .build();
    }
  • 继承GatewayFilterFactory
    如果定义局部过滤器,想在配置文件中进行配置来使用,可以继承AbstractGatewayFilterFactory抽象类或者AbstractNameValueGatewayFilterFactory
    整个体系结构为:
    在这里插入图片描述
    这两个抽象类的区别就是前者接收一个参数(像StripPrefix和我们创建的这种),后者接收两个参数(像AddResponseHeader)
    代码的编写可以参考:StripPrefixGatewayFilterFactory 和 AddRequestHeaderGatewayFilterFactory
    过滤器工厂默认命名规则必须按照"名称"+GatewayFilterFactory`,如上StripPrefixGatewayFilterFactory的过滤器名称为StripPrefix
  • 继承AbstractGatewayFilterFactory
    编写过滤器
@Slf4j
@Component //一定要将其交给spring容器管理
public class PayMethodGatewayFilterFactory extends AbstractGatewayFilterFactory<PayMethodGatewayFilterFactory.Config> {

    public PayMethodGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String paymethod = config.getPayMethod();
            String msg = config.getMsg();
            log.info("PayMethodGatewayFilterFactory 加载到的配置信息为:{}---{}",paymethod,msg);
            //将paymethod添加到请求头中
            exchange.getRequest().mutate().header("paymethod",paymethod);
            return chain.filter(exchange);
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return  Arrays.asList("payMethod","msg");//指定从yml中提前出来的配置信息填充到配置类中哪个属性,按规则配置
    }

    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.DEFAULT;//默认规则
    }

    /**
     * 加载从yml中提取出来的配置信息
     */
    @Data
    public static class Config {
        private String payMethod;
        private String msg;
    }
}

配置文件中使用

    gateway:
      #路由配置
      routes:
        #唯一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Path=/driver/**
            - Cookie=username,itheima
            - Header=token,^(?!\d+$)[\da-zA-Z]+$
            - Method=GET,POST
            - Token=Authorization
          filters:
            - PayMethod=alipay,业务整合
  • 继承AbstractNameValueGatewayFilterFactory
    直接查看AddRequestHeaderGatewayFilterFactory源码,分析即可

2.3.4 自定义GlobalFilter

定义全局过滤器需要实现GlobalFilter,Ordered接口

GlobalFilter:过滤器拦截处理方法
Ordered:过滤器也有多个,这里主要定义过滤器执行顺序,里面有个方法getOrder()会返回过滤器执行顺序,返回值越小,越靠前执行

创建全局过滤器并完成常见业务用户权限校验,如果请求中有带有一个名字为token的请求参数,则认为请求有效放行,如果没有则拦截提示授权无效

@Component
public class RouterFilter implements GlobalFilter,Ordered {

    /***
     * 路由拦截
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("RouterFilter----------------");
        //获取请求参数
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        //如果token为空,则表示没有登录
        if(StringUtils.isEmpty(token)){
            //没登录,状态设置403
            exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
            //结束请求
            return exchange.getResponse().setComplete();
        }
        //放行
        return chain.filter(exchange);
    }

    /***
     * 拦截器顺序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

2.4 跨域配置

2.4.1 yml方式

    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - PUT

2.4.1 @Bean配置

/**
 * 配置跨域
 * @return
 */
@Bean
public CorsWebFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    // cookie跨域
    config.setAllowCredentials(Boolean.TRUE);
    config.addAllowedMethod("*");
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    // 配置前端js允许访问的自定义响应头
    config.addExposedHeader("Authorization");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
    source.registerCorsConfiguration("/**", config);
    return new CorsWebFilter(source);
}

2.5 限流

当我们的系统 被频繁的请求的时候,就有可能 将系统压垮,所以 为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,那么就可以在网关系统做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中

2.5.1 令牌桶算法讲解

在这里插入图片描述
令牌桶算法的实现,有很多技术,Guaua是其中之一,redis客户端也有其实现

1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
2)根据限流大小,设置按照一定的速率往桶里添加令牌;
3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流

2.5.2 限流案例

spring cloud gateway 默认使用redis的RateLimter限流算法来实现
1、引入依赖

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>

同时不要忘记Redis配置:

  redis:
    host: 127.0.0.1
    port: 6379

2、定义KeyResolver
在Application引导类中添加如下代码,KeyResolver用于计算某一个类型的限流的KEY也就是说,可以通过KeyResolver来指定限流的Key
我们可以根据IP来限流,比如每个IP每秒钟只能请求一次,在GatewayApplication定义key的获取,获取客户端IP,将IP作为key,如下代码:

/***
 * IP限流
 * @return
 */
@Bean(name="ipKeyResolver")
public KeyResolver userKeyResolver() {
    return new KeyResolver() {
        @Override
        public Mono<String> resolve(ServerWebExchange exchange) {
            //获取远程客户端IP
            String hostName = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
            System.out.println("hostName:"+hostName);
            return Mono.just(hostName);
        }
    };
}

在路由中配置如下:

    gateway:
      #路由配置
      routes:
        #唯一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Path=/driver/**
          filters:
            - PayMethod=alipay,业务整合
            - name: RequestRateLimiter #请求数限流 名字不能随便写 ,使用默认的facatory
              args:
                key-resolver: "#{@ipKeyResolver}"
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 1

redis-rate-limiter.replenishRate 每秒生产的令牌数量
redis-rate-limiter.burstCapacity 令牌桶的最大容量
key-resolver “#{@ipKeyResolver}” 用于通过SPEL表达式来指定使用哪一个KeyResolver.
如上配置:表示 一秒内,允许 一个请求通过,令牌桶的填充速率也是一秒钟添加一个令牌

稳定速率是通过在replenishRate(补充令牌速度) 和 burstCapacity(令牌桶容量)中设置相同的值来实现的。可通过设置burstCapacity高于replenishRate来允许临时突发流浪(高于每秒的请求书将从桶中获取令牌)。在这种情况下,限流器需要在两次突发之间留出一段时间(根据replenishRate),因为连续两次突发将导致请求丢失 (HTTP 429 - Too Many Requests)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/613425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库的增删改查(2)

1、数据库约束 关系型数据库的一个重要功能 主要作用是保证数据的完整性&#xff0c;也就是数据的正确性&#xff08;数据本身是正确的&#xff0c;关联关系也是正确的&#xff09; 人工检查数据的完整性的工作量非常的大&#xff0c;在数据表定义一些约束&#xff0c;那数据…

Shamir 秘密共享、GMW和BGW方案

一、Shamir秘密共享 Shamir秘密共享方案是一种将秘密拆分成多份并分配给多个参与者保存&#xff0c;只有在满足特定条件下才能恢复原始秘密的密码学方案。它具有良好的容错性、加法同态性和无条件安全性等特点。 具体地&#xff0c;Shamir秘密共享方案可以概括为以下步骤&…

Protobuf数据类型

enum类型 语法⽀持我们定义枚举类型并使⽤。在.proto⽂件中枚举类型的书写规范为&#xff1a; 枚举类型名称&#xff1a; 使⽤驼峰命名法&#xff0c;⾸字⺟⼤写。例如&#xff1a; MyEnum 常量值名称&#xff1a; 全⼤写字⺟&#xff0c;多个字⺟之间⽤连接。例如&#xff1a…

【嵌入式烧录/刷写文件】-2.9-Intel Hex文件的地址对齐Address Alignment

案例背景(共5页精讲): 对一个Intel Hex文件&#xff0c;进行地址对齐Address Alignment。 目录 1 为什么要进行“地址对齐Address Alignment” 1.1 “对齐长度”的选择 2 使用Vector HexView工具对Hex文件进行“地址对齐Address Alignment” 2.1 “自动”完成“地址对齐Ad…

【JavaEE】Cookie与Session的前后端交互-表白墙登录设计

【JavaEE】Cookie与Session的前后端交互-表白墙登录设计 文章目录 【JavaEE】Cookie与Session的前后端交互-表白墙登录设计1. Cookie与Session1.1 后端doPost的实现1.1.1 隐藏的全局变量1.1.2 获取请求对应的HttpSession对象1.1.3 约定前后端交互方式1.1.4 验证用户名与密码是否…

你不知道的Bing聊天机器人:7个惊人的用途!

导读&#xff1a;以下是我总结的有用的方法&#xff0c;可以利用AI聊天机器人让您的生活更轻松。 本文字数&#xff1a;1600&#xff0c;阅读时长大约&#xff1a;10分钟 生成式AI工具可以用非常多的方式使你的日常生活更轻松。 AI聊天机器人在编程、写作等方面表现出色&#…

JVM入门:官网了解JVM、Java源文件运行过程、什么是类加载器、Java的类加载机制的三种方式、Tomcat的自定义类加载器

JVM入门&#xff1a;官网了解JVM、Java源文件运行过程、什么是类加载器、Java的类加载机制的三种方式、Tomcat的自定义类加载器 通过官网了解JVM官网jdk8结构图什么是JVM查看官网Java语言和虚拟机规范 Java源文件运行过程1.Java源文件经过Javac编译成字节码文件如何手动编译一个…

Java如何实现去重?这是在炫技吗?

大家好&#xff0c;我3y啊。由于去重逻辑重构了几次&#xff0c;好多股东直呼看不懂&#xff0c;于是我今天再安排一波对代码的解析吧。austin支持两种去重的类型&#xff1a;N分钟相同内容达到N次去重和一天内N次相同渠道频次去重。 Java开源项目消息推送平台&#x1f525;推送…

高性能服务器为什么需要内存池?

C/C程序为什么比起其它语言开发的程序效率要高&#xff0c;一个很重要的原因就是可以直接操作内存&#xff0c;今天就来讲讲为什么需要内存池的技术。 从一个示例开始 先看下面两段代码&#xff0c;都是去重复的创建和删除对象0x5FFFFF次&#xff0c;他们的执行后的效率怎么样…

DIP第7章知识点

文章目录 7.3 相关7.5 基图像7.6 傅里叶相关变换7.6.1 离散哈特利变换7.6.3 离散正弦变换 DIP的其他章节都好复习&#xff0c;唯独就这个第7章小波变换。复习起来十分头大&#xff0c;所以我开始写他的课后题&#xff0c;雾。 7.3 相关 已知两个连续函数 f ( x ) f(x) f(x) …

一篇博客教会你怎么使用Docker安装Redis

文章目录 1、搜索镜像2、拉取镜像3、下载配置文件4、修改配置文件5、启动容器 今天我们学习使用 Docker 安装 Redis。 1、搜索镜像 docker search redis2、拉取镜像 docker pull redis3、下载配置文件 wget http://download.redis.io/redis-stable/redis.conf下载的配置文件…

云原生Docker容器管理

docker容器相当于一个进程&#xff0c;性能接近于原生&#xff0c;几乎没有损耗&#xff1b; docker容器在单台主机上支持的数量成百上千&#xff1b; 容器与容器之间相互隔离&#xff1b; 镜像是创建容器的基础&#xff0c;可以理解镜像为一个压缩包 docker容器的管理 容器…

彩票中奖率的真相:用 JavaScript 看透彩票背后的随机算法

原本这篇文章是打算叫「假如我是彩票系统开发者」&#xff0c;但细想一下&#xff0c;如果在文章中引用太多的 JavaScript 的话&#xff0c;反而不是那么纯粹&#xff0c;毕竟也只是我的一厢情愿&#xff0c;彩票开发也不全如本文所讲&#xff0c;有所误导的话便也是得不偿失了…

理解 Java 关键字 final

原文链接 理解 Java 关键字 final final可以用来干什么 final是Java中非常常见的一个关键字&#xff0c;可以说每天都在使用它&#xff0c;虽然常见&#xff0c;但却也不见得都那么显而易见&#xff0c;今天就来研究一下final&#xff0c;以加深对它的理解和更合理的运用。 修…

gitstack使用教程

一、下载及安装 下载地址&#xff1a;https://gitstack.com/download/?spma2c4e.10696291.0.0.6d4c19a40qOauc 支持操作系统列表 本文基于2.3.12版本 下载完成后安装&#xff0c;默认安装路径为&#xff1a;c:\GitStack&#xff0c;安装过程中&#xff0c;gitstack服务会启…

医学图像分割 nnUNetV2 分割自定义2d数据集

文章目录 1 环境安装(Pytorch)1.1 安装conda1.1 安装pytorch1.3 安装nnUNet1.4 安装隐藏层&#xff08;可选&#xff09; 2 配置自定义数据集2.1 数据集格式2.2 创建需要目录2.3 数据格式转换2.3.1 修改路径与数据集名称2.3.2 修改训练集与测试集2.3.3 修改掩码所在的文件夹&am…

python基础----05-----函数的多返回值、函数的多种参数使用形式、函数作为参数传递、lambda匿名函数

一 函数的多返回值 if __name__ __main__:# 演示使用多个变量&#xff0c;接收多个返回值def test_return ():return 1,hello,Truex,y,z test_return()print(x)print(y)print(z)1helloTrue二 函数的多种参数使用形式 分为以下四种。 2.1 位置参数 位置参数调用函数时根据…

卡尔曼滤波与组合导航原理(五)序贯Kalman滤波

量测维数很高&#xff0c;而且能写成很多分量&#xff0c;每一个分量可以看成一个小量测&#xff0c;可以序贯进行量测更新 优点是&#xff1a;计算快&#xff0c;数字稳定性更好&#xff0c;我们知道矩阵求逆是和维数的三次方成正比&#xff0c;分成小矩阵求逆快&#xff08;都…

自学大语言模型之Bert和GPT的区别

Bert和GPT的区别 起源 2018 年&#xff0c;Google 首次推出 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;。该模型是在大量文本语料库上结合无监督和监督学习进行训练的。 BERT 的目标是创建一种语言模型&#xff0c;可以理解句子中…

(Day1)配置云开发提供的模板

创建云开发项目 打开微信开发者工具&#xff1b;点击“项目”->“新建项目”&#xff1b;输入项目名称和选择项目所要保存的目录&#xff1b;输入自己的AppID&#xff1b; AppID的获取&#xff0c;需要登陆微信公众平台&#xff0c;并点击“开发管理”->"开发设置…