Gateway使用手册

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,旨在提供微服务简单、高效的API路由管理方式,以及基于过滤器实现安全认证、监控和限流等功能。另外,就是取代Netflix停止更新的网关组件Zuul。

Spring Cloud Gateway基于WebFlux + Netty + Reactor实现响应式API网关,摒弃传统Servlet容器,Netty构建提供断点服务。

01 核心概念

概念 内容
路由(route) 路由是网关中最基础部分,路由信息包括ID、URI、一组谓词、一组Filter组成。如果谓词为真,则说明请求URL与配置路由匹配。
谓词(predicates) 也就是java.util.function.Predicate , Gateway使用Predicate实现路由匹配。
过滤器(Filter) Gateway分为Gateway FilIer和Global Filter,用于处理请求和响应。

Spring Cloud Gateway路由就是请求转发规则,谓词就是匹配路径条件,过滤器修改路由请求以及响应提供业务处理逻辑。

02 基础案例

2.1 依赖

xml 复制代码
<dependencies>
    <!-- Gateway网关 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--  Nacos注册中心 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- 排除web环境依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>test</scope> 
    </dependency>
</dependencies>

需要排除spring-boot-starter-web,也就是不用Servlet容器运行网关服务,否则会报错无法运行。

2.2 路由配置

yaml 复制代码
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          ## 是否开启微服务路由功能(默认false), 也就是通过微服务名称访问服务, 不建议打开
          ## 示例: http://localhost:13001/order/order/create
          enabled: true
      # 是否开启网关
      enabled: true

如果开启微服务路由功能,支持通过http://localhost:13001/nx-order/order/create访问服务(nx-order)资源(/order/create),为了安全考虑不建议开启。

2.3 测试

03 路由谓词

路由谓词(Predicate)起到路由筛选匹配作用,Gateway默认提供很多谓词支持,包括路径谓词(Path)、时间谓词(After、Before、Between)、Cookie谓词(Cookie)、请求头谓词(Header)、域名谓词(Host)、请求方法谓词(Method)、路径谓词(Path)、请求参数谓词(Query)和远程地址谓词(RemoteAddr)。

3.1 路由配置

3.1.1 路由到指定URL

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: {唯一标识}
        uri: http://localhost:8001/
        predicates:
            - Path=/order/*

3.1.2 路由到注册中心微服务

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: {唯一标识}
        uri: lb://nx-order/
        predicates:
            - Path=/order/*

3.2 谓词工厂分类

谓词类别 谓词类
时间谓词 AfterRoutePredicateFactoryBeforeRoutePredicateFactoryBetweenRoutePredicateFactory
Cookie谓词 CookieRoutePredicateFactory
请求头谓词 HeaderRoutePredicateFactoryHostRoutePredicateFactory
请求谓词 MethodRoutePredicateFactoryPathRoutePredicateFactoryQueryRoutePredicateFactoryRemoteAddrRoutePredicateFactoryWeightRoutePredicateFactory

3.2.1 After路由断言工厂

AfterRoutePredicateFactory需要配置UTC格式时间参数,请求时间与配置时间相比,若请求时间在参数时间之后,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: lb://nx-user
          predicates:
            # 当且仅当请求时间After配置时间, 才会转发到用户微服务, 否则不匹配返回404
            # 例如: http://localhost:8040/users/1 -> nx-user/users/1
            - After=2024-04-30T17:42:47.789-07:00[America/Denver]

3.2.2 Before路由断言工厂

BeforeRoutePredicateFactory需要配置UTC格式时间参数,请求时间与配置时间相比,若请求时间在参数时间之前,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: lb://nx-user
          predicates:
            # 当且仅当请求时间After配置时间, 才会转发到微服务, 否则不匹配返回404
            # 例如: http://localhost:8040/users/1 -> nx-user/users/1
            - After=2024-04-30T17:42:47.789-07:00[America/Denver]
yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: lb://nx-user
          predicates:
            # 当且仅当请求时间Before配置时间, 才会转发到微服务, 否则不匹配返回404
            # 例如: http://localhost:8040/users/1 -> nx-user/users/1
            - Before=2024-04-30T17:42:47.789-07:00[America/Denver]

3.2.3 Between路由断言工厂

BetweenRoutePredicateFactory需要配置两个UTC格式时间参数,请求时间需要处于两个参数时间之间,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: between_route
          uri: lb://x-user
          predicates:
            # 当且仅当请求时间Between配置时间, 才会转发到微服务, 否则不匹配返回404
            - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2027-01-21T17:42:47.789-07:00[America/Denver]

3.2.4 Cookie路由断言工厂

CookieRoutePredicateFactory包含两个参数,分别是cookie 的key与value。当请求携带指定key与value的Cookie时,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: cookie_route
          uri: lb://nx-user
          predicates:
            ## cookie[domain=ch.p]
            - Cookie=domain, ch.p

3.2.5 Header路由断言工厂

HeaderRoutePredicateFactory包含两个参数,分别是请求头header的key与value。当请求携带指定key与value的header时,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: header_route
          uri: lb://nx-user
          predicates:
            # 当且仅当携带请求头X-Request-Id, 并且值符合正则\d+时,才会转发到微服务
            - Header=X-Request-Id, \d+

3.2.6 Host路由断言工厂

HostRoutePredicateFactory包含请求头Host属性。当请求携带指定Host属性值时,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: host_route
          uri: lb://nx-user
          predicates:
            # 当且仅当名请求头Host符合**.somehost.org或**.anotherhost.org时, 才会转发微服务
            - Host=**.somehost.org,**.anotherhost.org

3.2.7 Method路由断言工厂

MethodRoutePredicateFactory用于判断请求是否使用指定请求方法,比如是POST、GET等。当请求中使用指定请求方法时,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: method_route
          uri: lb://nx-user
          predicates:
            # 当且仅当HTTP请求方法是GET时, 才会转发微服务
            - Method=GET

3.2.8 Path路由断言工厂

PathRoutePredicateFactory用于判断请求路径是否包含指定URI。若包含匹配成功断言为true,此时会将匹配上URI拼接到要转向目标URI后面,形成统一URI。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://nx-user
          predicates:
            # 当且仅当访问路径是/users/*或者/some-path/**,才会转发微服务
            - Path=/users/{segment},/some-path/**

3.2.9 Query路由断言工厂

QueryRoutePredicateFactory用于查找请求指定参数。其可以只查看参数名称,也可以同时查看参数名与参数值。当请求包含指定参数名或名值对匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: query_route
          uri: lb://nx-user
          predicates:
            # 当且仅当请求带有baz的参数,才会转发到微服务
            # eg. 访问http://localhost:8040/users/1?baz=xx -> nx-user/users/1?baz=xx
            - Query=baz
yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: query_route
          uri: lb://msb-user
          predicates:
            # 当且仅当请求带有名为foo参数, 且参数值符合正则ba., 才会转发到微服务
            # eg. 访问http://localhost:8040/users/1?baz=baz -> nx-user/users/1?baz=baz
            - Query=foo, ba.

3.2.10 RemoteAddr路由断言工厂

RemoteAddrRoutePredicateFactory用于判断访问IP地址是否在断言中指定IP范围。当访问目标IP在指定范围时,匹配成功,断言为true。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: remoteaddr_route
          uri: lb://nx-user
          predicates:
            # 当且仅当请求IP是192.168.1.1/24网段, 例如192.168.1.10, 才会转发到用户微服务
            # eg. 访问http://localhost:8040/users/1 -> nx-user/users/1
            - RemoteAddr=192.168.1.1/24

如果Spring Cloud Gateway位于反向代理Nginx后面,获取到远程地址可能不正确,此时可以自定义RemoteAddressResolver解决。

3.2.11 Weight路由断言工厂

WeightRoutePredicateFactory包含两个参数,分别表示组(group)与权重(weight)。对于同一组多个 URI地址,路由器会根据设置权重,按比例将请求转发给相应的URI实现负载均衡。

yaml 复制代码
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

3.3 自定义谓词

自定义谓词类命名必须以RoutePredicateFactory结尾,使用前缀进行配置。

java 复制代码
@Data
public class CustomCfg {
    private LocalTime start;
    private LocalTime end;
}

@Component
public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomCfg> {
    public TimeBetweenRoutePredicateFactory() {
        super(TimeBetweenConfig.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(TimeBetweenConfig config) {
        LocalTime start = config.getStart();
        LocalTime end = config.getEnd();
        
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                LocalTime now = LocalTime.now();
                return now.isAfter(start) && now.isBefore(end);
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("start","end");
    }
}
yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://nx-user
          predicates:
            - Path=/order/*
            - Custom=上午08:00, 下午11:00

04 过滤器工厂

Spring Cloud Gateway内置很多过滤器工厂(GatewayFilter Factories),使用过滤器工厂可以进行业务逻辑处理器,比如添加/剔除响应头,添加/去除参数等。

4.1 内置过滤器

4.1.1 AddRequestHeader

为原始请求添加名为X-Request-Foo=Bar请求头。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

4.1.2 AddRequestParameter

为原始请求添加请求参数foo=bar

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=foo, bar

4.1.3 AddResponseHeader

为处理添加X-Request-Foo=Bar响应头

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        filters:
        - AddResponseHeader=X-Response-Foo, Bar

4.1.4 DedupeResponseHeader

Spring Cloud Greenwich SR2提供DedupeResponseHeader新特性,低于这个版本无法使用。可以阅读DedupeResponseHeaderGatewayFilterFactory注释了解功能,比官方文档写得还好。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: dedupe_response_header_route
        uri: https://example.org
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST

如果Gateway、微服务设置CORS(跨域)请求头不做任何配置,那么通过请求 -> 网关 -> 微服务响应数据头数据就会重复。

yaml 复制代码
Access-Control-Allow-Credentials: true, true
Access-Control-Allow-Origin: https://www.example.com, https://www.example.com

通过配置DedupeResponseHeader,就可以实现剔除重复响应数据值。

yaml 复制代码
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

另外,去重Header如果有多个,可以使用空格分隔即可,去重策略包括RETAIN_FIRST、RETAIN_LAST和RETAIN_UNIQUE。

去重策略 内容
RETAIN_FIRST 默认值,保留第一个值
RETAIN_LAST 保留最后一个值
RETAIN_UNIQUE 保留所有唯一值,以第一次出现顺序保留

4.1.5 PrefixPath

为匹配路由添加前缀。例如:访问${GATEWAY_URL}/hello会转发到https://example.org/mypath/hello

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - PrefixPath=/mypath

4.1.6 PreserveHostHeader

如果没有设置,那么通过Http Client控制请求头Host。如果已设置,那么会额外设置一个请求属性(preserveHostHeader=true),路由过滤器会检查从而判断是否要发送原始请求头Host。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: https://example.org
        filters:
        - PreserveHostHeader

4.1.7 RequestRateLimiter

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 10
            redis-rate-limiter.burstCapacity: 20

4.1.8 RedirectTo

重定向过滤工厂配置格式RedirectTo=status, URL [, includeRequestParams],通过带有300系列请求头Location:URL执行重定向,也可以指定重定向是否带有请求参数(未指定默认为false)。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        # https://example.org?skip=10 => https://acme.org
        - RedirectTo=302, http://www.acme.com, false

4.1.9 RemoveHopByHop

移除转发请求头,多个使用逗号(,)分隔,能够移除请求头包括Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、TE、Trailer、Transfer-Encoding和Upgrade。

csharp 复制代码
spring.cloud.gateway.filter.remove-hop-by-hop.headers: Connection,Keep-Alive

4.1.10 RemoveRequestHeader

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

4.1.11 RemoveResponseHeader

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: https://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo

4.1.12 RewritePath

重写请求路径,(?<segment>/?.*)表示/?.*匹配结果捕获到segment组,${segment}表示置换segment分组文本到此处。

需要注意,$是避免YAML语法认为此处为变量,网关解析时会被替换为${segment}。

正则规则 内容
?<segment> 规则组
? 出现0次或者1次
.* 任意字符出现0次或多次
yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
        # http://gateway/foo/uml/index.html => https://example.org/uml/index.html
        - RewritePath=/foo/(?<segment>.*), /${segment}

4.1.13 RewriteResponseHeader

下游请求头X-Response-Foo 值为user=ford&password=omg!what&flag=true,将被重置为user=ford&password=***&flag=true。需要注意,YAML规范必须使用$来表示$

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: https://example.org
        filters:
        - RewriteResponseHeader=X-Response-Foo, password=[^&]+, password=***

4.1.14 SaveSession

请求转发后端微服务之前,强制执行WebSession::save 操作。用于Spring Session延迟数据存储,并希望请求转发前保存会话状态情况,比如集成使用Spring Secutiry与Spring Session传递会话信息给校友服务器。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

4.1.15 SecureHeaders

添加一系列安全作用响应头,Spring Cloud Gateway主要参考博客为https://blog.appcanary.com/2017/http-security-headers.html引入。

如果需要修改默认值,可以通过spring.cloud.gateway.filter.secure-headers.[请求头]=值进行修改。另外,也可以使用spring.cloud.gateway.filter.secure-headers.disable=[请求头,...]禁用配置。

默认请求头 请求头值
X-Xss-Protection mode=block
Strict-Transport-Security max-age=631138519
X-Frame-Options DENY
X-Content-Type-Options nosniff
Referrer-Policy no-referrer
Content-Security-Policy default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
X-Download-Options noope
X-Permitted-Cross-Domain-Policie none

4.1.16 SetPath

通过请求路径片段模板化,达到操作修改路径目的。比如访问${GATEWAY_PATH}/foo/bar ,后端微服务路径修改为/bar

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: https://example.org
        predicates:
        - Path=/foo/{segment}
        filters:
        - SetPath=/{segment}

4.1.17 SetResponseHeader

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: http://example.org
        filters:
        - SetResponseHeader=X-Response-Foo, Bar

如果后端服务响应带有名为 X-Response-Foo 的响应头,则将值改为替换成 Bar

4.1.18 SetStatus

修改响应的状态码,值可以是数字,也可以是字符串,但是状态值必须是Spring HttpStatus枚举类的值。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: http://example.org
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route
        uri: http://example.org
        filters:
        - SetStatus=401

4.1.19 StripPrefix

数字表示要截断路径数量。如果请求路径为/name/bar/foo ,则路径会修改为/foo ,也就是会截断至2个路径。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: http://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

4.1.20 Retry

针对不同响应做重试

参数 内容
retries 重试次数
statuses 重试状态码,可选值查看org.springframework.http.HttpStatus
methods 重试请求方法,可选值查看org.springframework.http.HttpMethod
series HTTP状态码系列,可选值查看org.springframework.http.HttpStatus.Series
yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY

4.1.21 RequestSize

设置后端服务收到最大请求包大小,默认值为5M。如果请求超过设置值,返回413 Payload Too Large 。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
      uri: http://localhost:8080/upload
      predicates:
      - Path=/upload
      filters:
      - name: RequestSize
        args:
          # 单位字节
          maxSize: 5000000

4.1.22 默认过滤器工厂

yaml 复制代码
spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
      - PrefixPath=/httpbin

4.2 AddRequestHeader

断点打在org.springframework.cloud.gateway.filter.NettyRoutingFilter#filter ,就可以调试Gateway转发具体细节。

yaml 复制代码
logging:
  level:
    org.springframework.cloud.gateway: trace
    org.springframework.http.server.reactive: debug
    org.springframework.web.reactive: debug
    reactor.ipc.netty: debug

4.3 自定义过滤器工厂

Gateway底层基于Netty构建,封装请求和响应封装到ServerWebExchange,然后进行过滤器(GlobalFilter)拦截处理请求。

4.3.1 过滤器工厂定义

java 复制代码
public interface GatewayFilter extends ShortcutConfigurable {
	String NAME_KEY = "name";
    
	String VALUE_KEY = "value";
    
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
核心API 内容
exchange.getRequest().mutate() 修改请求
exchange.mutate() 修改Exchange
chain.filter(exchange) 传递下一个过滤器处理
exchange.getResponse() 获取响应

4.3.2 自定义过滤器工厂

自定义过滤器工厂可以继承AbstractGatewayFilterFactory,可以参考RequestSizeGatewayFilterFactory实现类。另外,AbstractNaeValueGatewayFilterFactory底层扩展实现AbstractGatewayFilterFactory,简化过滤器操作,主要参考类是AddRequestHeaderGatewayFilterFactory。

需要注意的是,必须以GatewayFilterFactory结尾定义过滤器工厂,配置文件才能被Gateway识别和操作。

java 复制代码
@Slf4j
@Component
public class PrintLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        // 这里应用启动会执行两次, 需要放到方法里面
        //  log.info("request info={}:{}",config.getName(), config.getValue());
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("request info={}:{}",config.getName(),config.getValue());
                // 获取请求进行修改
                ServerHttpRequest modifiedRequest = exchange.getRequest().mutate().build();
                // 设置请求值
                // ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
                //                   header(config.getName(), value).build();
                ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();
                return chain.filter(modifiedExchange);
            }
        };
    }
}
yaml 复制代码
spring:
  application:
    name: nx-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: false
      enabled: true
      routes:
        - id: order-route
          uri: lb://nx-order
          predicates:
            - Path=/order/*
          filters:
            - PrintLog=sex,man

4.4 全局过滤器

Spring Cloud Gateway组件GatewayFilter仅支持特定路由,如果需要全局过滤器可以配置GlobalFilter,可以参考文档docs.spring.io/spring-clou...

4.4.1 LoadBalancerClientFilter

如果URL具有LB方案(例如lb://myservice),Gateway就会通过LoadBalancerClientFilter使用LoadBalancerClient解析服务名称为实际主机与端口,并且缓存原始URL到请求 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR属性。

yaml 复制代码
spring:
    gateway:
      routes:
        - id: order-route
          uri: lb://nx-order
          predicates:
            - Path=/order/*
java 复制代码
public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求地址
		URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
		String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
        // 非lb协议
		if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
			return chain.filter(exchange);
		}
        
		addOriginalRequestUrl(exchange, url);

		if (log.isTraceEnabled()) {
			log.trace("LoadBalancerClientFilter url before: " + url);
		}
        // 负载均衡选择实例
		final ServiceInstance instance = choose(exchange);
		if (instance == null) {
			throw NotFoundException.create(properties.isUse404(), ...);
		}
		URI uri = exchange.getRequest().getURI();
		String overrideScheme = instance.isSecure() ? "https" : "http";
		if (schemePrefix != null) {
			overrideScheme = url.getScheme();
		}

        // 获取请求URI
		URI requestUrl = loadBalancer.reconstructURI(
				new DelegatingServiceInstance(instance, overrideScheme), uri);
        
		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
		return chain.filter(exchange);
	}

    protected ServiceInstance choose(ServerWebExchange exchange) {
		return loadBalancer.choose(
				((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
	}
}

4.4.2 自定义全局过滤器

全局过滤器实现GlobalFilter接口,实现Order接口确定过滤器执行顺序,值越小越靠前处理。

java 复制代码
@Slf4j
@Component
public class IPAddressStatisticsFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        InetSocketAddress host = exchange.getRequest().getHeaders().getHost();
        if (host == null || host.getHostName() == null) {
            exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
            return exchange.getResponse().setComplete();
        }
        String hostName = host.getHostName();
        AtomicInteger count = IpCache.CACHE.getOrDefault(hostName, new AtomicInteger(0));
        count.incrementAndGet();
        IpCache.CACHE.put(hostName, count);
        log.info("ip = {}, request count={}", hostName, count.intValue());
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
相关推荐
pany12 分钟前
体验一款编程友好的显示器
前端·后端·程序员
Java水解18 分钟前
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
后端·spring
开始学java23 分钟前
继承树追溯
后端
何中应28 分钟前
分布式事务的两种解决方案
java·分布式·后端
SimonKing1 小时前
无需重启!动态修改日志级别的神技,运维开发都哭了
java·后端·程序员
架构精进之路1 小时前
多智能体系统不是银弹
后端·架构·aigc
涡能增压发动积2 小时前
MySQL数据库为何逐渐黯淡,PostgreSQL为何能新王登基
人工智能·后端
架构精进之路2 小时前
多智能体系统架构解析
后端·架构·ai编程
Java中文社群2 小时前
重磅!Ollama发布UI界面,告别命令窗口!
java·人工智能·后端