User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Spring   ›   SpringCloud   ›   Gateway   ›   正文
Gateway

SpringCloud—GateWay(一)

2020-08-27 10:22:54
1410  0 3
参考目录 隐藏
1) Spring Cloud Gateway
2) 相关概念:
3) 基本用法
4) 代码配置
5) properties 配置
6) YML 配置
7) 跨域支持
8) 方式一(推荐,尽可能把网关升级到2.2.x以上版本,低版本很多不完善到地方)
9) 方式二(针对SpringCloudGateway2.1.x及以下版本)
10) 方式三(针对SpringCloudGateway2.1.x及以下版本)
11) 超时时间配置
12) 配置说明
13) Netty线程池优化
14) 接口耗时日志

阅读完需:约 21 分钟

Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets,Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性。


Spring Cloud Gateway

这里Gateway的版本是3.0.4,cloud是2020版本的。

官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

相关概念:

  • Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
  • Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
  • Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。

Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的区别就是 Gateway 的 Filter 只有 pre 和 post 两种。

客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

Spring Cloud Gateway 的特征:

  • 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
  • 动态路由
  • Predicates 和 Filters 作用于特定路由
  • 集成 Hystrix 断路器
  • 集成 Spring Cloud DiscoveryClient
  • 易于编写的 Predicates 和 Filters
  • 限流
  • 路径重写

基本用法

Spring Cloud Gateway 网关路由有两种配置方式:

  • 在配置文件 yml 中配置
  • 通过@Bean自定义 RouteLocator,在启动主类 Application 中配置

这两种方式是等价的,建议使用 yml 方式进配置。

引入依赖:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

Spring Cloud Gateway 是使用 netty+webflux 实现因此不需要再引入 web 模块。


代码配置

我们可以在启动类 GateWayApplication 中添加方法 routeLocator() 来定制转发规则。

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

    /**
     * 自定义路由
     */
    @Bean
    RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("javaboy_route", r -> r.path("/get").uri("http://httpbin.org"))
                .build();
    }

}

上面配置了一个 id 为 path_route 的路由,当访问地址http://localhost:8080/get 时会自动转发到地址:http://www.httpbin.org/get和上面的转发效果一样,只是这里转发的是以项目地址/get 格式的请求地址。

properties 配置

spring.cloud.gateway.routes[0].id=javaboy_route
spring.cloud.gateway.routes[0].uri=http://httpbin.org
spring.cloud.gateway.routes[0].predicates[0]=Path=/get

因为routes可以是多个的所以 properties 中要以数组形式存在。

YML 配置

spring:
  cloud:
    gateway:
      routes:
         - id: javaboy_route # 路由唯一标识 不允许重复
          uri: http://httpbin.org # uri可以是http 也可以是 eureka的服务地址
          predicates: 
            - Path=/get  # 路由的前缀 这里一般只配置一个
          filters: # 过滤器 内置了不少通用的过滤器 如以下的重写path的过滤器
            - RewritePath=/business(?<segment>.*), /api$\{segment}
  • id:我们自定义的路由 ID,保持唯一
  • uri:目标服务地址
  • predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
  • filters:过滤规则,本示例暂时没用。

上面这段配置的意思是,配置了一个 id 为 javaboy_route 的路由规则,当访问地址 http://localhost:8080/get时会自动转发到地址:http://www.ityouknow.com/get。配置完成启动项目即可在浏览器访问进行测试,当我们访问地址http://localhost:8080/get 时会展示页面。

完整案例

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
#############################新增网关配置###########################
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
####################################################################

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

通过以上两种方式,都可以使路由生效。

但是在实际生产实践中,我们一般很少通过操作api来配置路由。

我们一般是通过配置的方式来配置路由,好处是更加灵活,调整路由不需要改动代码,修改配置即可。

但以上结论也并非绝对,针对一些大公司,路由可能非常复杂,并且路由规则是在外部存储中保存的,便于做后台管理界面去管理这些路由,针对这种情况,还是会通过api的方式来配置路由。


跨域支持

互联网公司的系统基本上都是前后端分离的,如果不支持跨域,则当前端域名和后端暴露接口域名不完全一致时,前端就无法正常请求接口,这个时候,就需要后端支持跨域,而对跨域的支持,正常情况下都是在网关层面做支持,故在spring cloud gateway中支持跨域是很常见的场景。

方式一(推荐,尽可能把网关升级到2.2.x以上版本,低版本很多不完善到地方)

针对SpringCloudGateway2.2.x以上版本 可以直接使用自带的跨域配置即可如下(直接在application.yaml添加配置)

spring:
  cloud:
    gateway:
      filter:
        remove-hop-by-hop:
          headers:
          # 以下是去掉网关默认去掉的请求响应头
          - trailer
          - te
          - keep-alive
          - transfer-encoding
          - upgrade
          - proxy-authenticate
          - connection
          - proxy-authorization
          - x-application-context
          # 以下是去掉服务层面定义的跨域
          - access-control-allow-credentials
          - access-control-allow-headers
          - access-control-allow-methods
          - access-control-allow-origin
          - access-control-max-age
          - vary
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowCredentials: true # 是否允许携带cookie
            allowedOrigins: "*" # 允许哪些网站的跨域请求
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowedMethods: "*" # 允许的跨域ajax的请求方式 如GET,POST
            maxAge: 3628800 # 这次跨域检测的有效期

在springboot版本大于2.4时会爆出一下错误

When allowCredentials is true, allowedOrigins cannot contain the 
special value "*“since that cannot be set on the “Access-Control-Allow-Origin”
 response header. To allow credentials to a set of origins, list them explicitly or 
consider   using"allowedOriginPatterns” instead.

翻译过来就是

当allowCredentials为true时,allowedOrigins不能包含特殊值“*”,因为不能在“Access Control Allow Origin”响应头上设置该值。要允许凭据指向一组源,请显式列出它们,或者考虑改用“allowedOriginPatterns”。

解决办法:跨域配置报错,将.allowedOrigins替换成.allowedOriginPatterns即可。

    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowCredentials: true
            allowedOriginPatterns: "*"
            allowedHeaders: "*"
            allowedMethods: "*"
            maxAge: 3628800
        add-to-simple-url-handler-mapping: true

方式二(针对SpringCloudGateway2.1.x及以下版本)

@Bean
public WebFilter corsFilter() {
    return (exchange, chain) -> {
        ServerHttpRequest request = exchange.getRequest();
        if (!CorsUtils.isCorsRequest(request)) {
            return chain.filter(exchange);
        }
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = response.getHeaders();
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "POST,GET,OPTIONS,DELETE,PUT");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "content-type");
        headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3628800");
        return chain.filter(exchange);
    };
}

方式三(针对SpringCloudGateway2.1.x及以下版本)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * 跨域配置
 *
 * @author jacky
 * @since 2019/08/14
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", buildConfig());
        return new CorsWebFilter(source);
    }

    private CorsConfiguration buildConfig(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();

        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("content-type");
        corsConfiguration.addAllowedMethod("POST,GET,OPTIONS,DELETE,PUT");
        corsConfiguration.setMaxAge(3628800L);

        return corsConfiguration;
    }
}

通过以上方式配置之后可能还会存在某些浏览器跨域存在问题,比如搜狗浏览器可能就会报跨域问题,导致请求异常,这个时候需要增加多一个过滤器即可解决,如下:

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;

/**
 * 跨域的 响应头处理 源码中有bug 需要通过该Filter处理
 *
 * @author jacky
 * @since 2019/08/15
 */
@Component
public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {

    @Override
    public int getOrder() {
        // 指定此过滤器位于NettyWriteResponseFilter之后
        // 即待处理完响应体后接着处理响应头
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return chain.filter(exchange).then(Mono.defer(() -> {
            exchange.getResponse().getHeaders().entrySet().stream()
                .filter(kv -> kv.getValue() != null && kv.getValue().size() > 1)
                .filter(kv -> kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
                    || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS))
                .forEach(kv -> kv.setValue(Arrays.asList(kv.getValue().get(0))));

            return chain.filter(exchange);
        }));
    }
}

值得注意的是,在servlet下跨域配置类是CorsFilter,位于package org.springframework.web.filter;包下。

在webFlux 响应式下跨域配置类变成了CorsWebFilter,位于package org.springframework.web.cors.reactive;包下。


超时时间配置

路由超时配置

可以为所有路由配置Http超时(响应和连接),并为每个特定路由覆盖Http超时。

全局路由超时时间配置

要配置全局http超时,需要配置以下两个参数:

connect-timeout 必须以毫秒为单位指定连接超时时间.
response-timeout 必须指定为java.time.Duration

示例如下:

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s

每个路由的超时时间配置

可以通过路由的metadata以下两个参数配置每个路由超时:
connect-timeout 必须以毫秒为单位指定连接超时时间.
response-timeout 必须以毫秒为单位指定响应超时时间.

示例如下:

- id: per_route_timeouts
  uri: https://example.org
  predicates:
    - name: Path
      args:
        pattern: /delay/{timeout}
  metadata:
    response-timeout: 200
    connect-timeout: 200

配置说明

配置说明请参考官网

配置名称 默认值 描述说明
spring.cloud.gateway.default-filters 应用于每个路由的过滤器定义列表。
spring.cloud.gateway.discovery.locator.enabled false 启用DiscoveryClient网关集成的标志。如果启用,则会默认把eureka的服务通过网关暴露出去,所以通常情况下我们都不会启用,而是根据需要去配置路由。
spring.cloud.gateway.discovery.locator.filters
spring.cloud.gateway.discovery.locator.include-expression true 将评估是否在网关集成中包括服务的SpEL表达式,默认为:true。
spring.cloud.gateway.discovery.locator.lower-case-service-id false 谓词和过滤器中的小写serviceId选项,默认为false。当eureka自动将serviceId大写时,对eureka很有用。因此MYSERIVCE将与/ myservice/**匹配
spring.cloud.gateway.discovery.locator.predicates
spring.cloud.gateway.discovery.locator.route-id-prefix routeId的前缀,默认为DiscoveryClient.getClass()。getSimpleName()+“ _”。服务ID将被添加以创建routeId。
spring.cloud.gateway.discovery.locator.url-expression ‘lb://‘+serviceId 为每个路由创建uri的SpEL表达式,默认为:’lb://‘+serviceId。
spring.cloud.gateway.enabled true 启用网关功能。
spring.cloud.gateway.fail-on-route-definition-error true 在路由定义错误时失败的选项,默认为true。否则,将记录警告。
spring.cloud.gateway.filter.remove-hop-by-hop.headers
spring.cloud.gateway.filter.remove-hop-by-hop.order
spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key true 如果密钥解析器返回空密钥,则切换为拒绝请求,默认为true。
spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code denyEmptyKey为true时返回的HttpStatus,默认为FORBIDDEN。
spring.cloud.gateway.filter.secure-headers.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’
spring.cloud.gateway.filter.secure-headers.content-type-options nosniff
spring.cloud.gateway.filter.secure-headers.disable
spring.cloud.gateway.filter.secure-headers.download-options noopen
spring.cloud.gateway.filter.secure-headers.frame-options DENY
spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies none
spring.cloud.gateway.filter.secure-headers.referrer-policy no-referrer
spring.cloud.gateway.filter.secure-headers.strict-transport-security max-age=631138519
spring.cloud.gateway.filter.secure-headers.xss-protection-header 1 ; mode=block
spring.cloud.gateway.forwarded.enabled true 启用ForwardedHeadersFilter。
spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping false 是否将全局CORS配置添加到URL处理程序。
spring.cloud.gateway.globalcors.cors-configurations
spring.cloud.gateway.httpclient.connect-timeout 连接超时(毫秒),默认值为45秒。
spring.cloud.gateway.httpclient.max-header-size 最大响应头大小。
spring.cloud.gateway.httpclient.max-initial-line-length 最大初始行长度。
spring.cloud.gateway.httpclient.pool.acquire-timeout 仅对于FIXED类型,等待获取的最长时间(毫秒)。
spring.cloud.gateway.httpclient.pool.max-connections 仅适用于FIXED类型,即在现有连接上启动挂起获取之前的最大连接数。
spring.cloud.gateway.httpclient.pool.max-idle-time 通道关闭后的时间(毫秒)。如果为空,则没有最长空闲时间。
spring.cloud.gateway.httpclient.pool.max-life-time 关闭通道的持续时间。如果为空,则没有最长生存时间。
spring.cloud.gateway.httpclient.pool.name proxy 通道池映射名称默认为代理。
spring.cloud.gateway.httpclient.pool.type HttpClient要使用的池类型,默认为ELASTIC。
spring.cloud.gateway.httpclient.proxy.host Netty HttpClient代理配置的主机名。
spring.cloud.gateway.httpclient.proxy.non-proxy-hosts-pattern 已配置主机列表的正则表达式(Java)。应该绕过代理直接到达
spring.cloud.gateway.httpclient.proxy.password Netty HttpClient代理配置的密码。
spring.cloud.gateway.httpclient.proxy.port Netty HttpClient的代理配置端口。
spring.cloud.gateway.httpclient.proxy.username Netty HttpClient代理配置的用户名。
spring.cloud.gateway.httpclient.response-timeout 响应超时时间
spring.cloud.gateway.httpclient.ssl.close-notify-flush-timeout 3000ms SSL close_notify 刷新超时时间。默认为3000毫秒。
spring.cloud.gateway.httpclient.ssl.close-notify-flush-timeout-millis
spring.cloud.gateway.httpclient.ssl.close-notify-read-timeout SSL关闭通知读取超时。默认为0毫秒。
spring.cloud.gateway.httpclient.ssl.close-notify-read-timeout-millis
spring.cloud.gateway.httpclient.ssl.default-configuration-type 默认ssl配置类型。默认为TCP。
spring.cloud.gateway.httpclient.ssl.handshake-timeout 10000ms SSL握手超时。默认为10000毫秒
spring.cloud.gateway.httpclient.ssl.handshake-timeout-millis
spring.cloud.gateway.httpclient.ssl.key-password 密钥密码,默认值与keyStorePassword相同。
spring.cloud.gateway.httpclient.ssl.key-store Netty HttpClient的密钥库路径。
spring.cloud.gateway.httpclient.ssl.key-store-password 密钥库密码。
spring.cloud.gateway.httpclient.ssl.key-store-provider Netty HttpClient的密钥库提供程序,可选字段。
spring.cloud.gateway.httpclient.ssl.key-store-type JKS Netty HttpClient的密钥库类型,默认为JKS。
spring.cloud.gateway.httpclient.ssl.trusted-x509-certificates 用于验证远程终结点的证书的受信任证书。
spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager false 安装netty UnsecureTrustManagerFactory。这是不安全的,不适合生产。
spring.cloud.gateway.httpclient.websocket.max-frame-payload-length 最大帧有效载荷长度。
spring.cloud.gateway.httpclient.websocket.proxy-ping true 代理ping帧到下游服务,默认为true。
spring.cloud.gateway.httpclient.wiretap false 启用Netty HttpClient的窃听调试。
spring.cloud.gateway.httpserver.wiretap false 启用Netty HttpServer的窃听调试。
spring.cloud.gateway.loadbalancer.use404 false
spring.cloud.gateway.metrics.enabled true 启用度量数据的收集。
spring.cloud.gateway.metrics.tags 添加到度量的标记映射.
spring.cloud.gateway.redis-rate-limiter.burst-capacity-header X-RateLimit-Burst-Capacity 返回突发容量配置的标头的名称。
spring.cloud.gateway.redis-rate-limiter.config
spring.cloud.gateway.redis-rate-limiter.include-headers true 无论是否包含包含速率限制器信息的头,默认值为true。
spring.cloud.gateway.redis-rate-limiter.remaining-header X-RateLimit-Remaining 返回当前秒期间剩余请求数的标头的名称。
spring.cloud.gateway.redis-rate-limiter.replenish-rate-header X-RateLimit-Replenish-Rate 返回补充率配置的标头的名称。
spring.cloud.gateway.redis-rate-limiter.requested-tokens-header X-RateLimit-Requested-Tokens 返回请求的令牌配置的标头的名称。
spring.cloud.gateway.routes 路由列表
spring.cloud.gateway.set-status.original-status-header-name 包含代理请求的http代码的header的名称。
spring.cloud.gateway.streaming-media-types
spring.cloud.gateway.x-forwarded.enabled true 如果XForwardedHeadersFilter已启用。
spring.cloud.gateway.x-forwarded.for-append true 如果启用了附加X-Forwarded-For作为列表。
spring.cloud.gateway.x-forwarded.for-enabled true 如果启用了X-Forwarded-For。
spring.cloud.gateway.x-forwarded.host-append true 如果启用了附加X-Forwarded-Host作为列表。
spring.cloud.gateway.x-forwarded.host-enabled true 如果启用了X-Forwarded-Host。
spring.cloud.gateway.x-forwarded.order 0 XForwardedHeadersFilter的顺序。
spring.cloud.gateway.x-forwarded.port-append true 如果启用了附加X-Forwarded-Port作为列表。
spring.cloud.gateway.x-forwarded.port-enabled true 如果启用了X-Forwarded-Port。
spring.cloud.gateway.x-forwarded.prefix-append true 如果启用了附加X-Forwarded-Prefix作为列表。
spring.cloud.gateway.x-forwarded.prefix-enabled true 如果启用了X-Forwarded-Prefix。
spring.cloud.gateway.x-forwarded.proto-append true 如果启用X-PRODIED-PROTO作为一个列表。
spring.cloud.gateway.x-forwarded.proto-enabled true 如果启用了X-Forwarded-Proto。

Netty线程池优化

要设置起本身可同时工作的线程数需要设置netty中的reactor.netty.ioWorkerCount参数。该参数无法直接配置,需要通过System.setProperty设置,故我们可以创建以下配置类来配置该参数:

@Configuration
public static class ReactNettyConfiguration {
 
    @Bean
    public ReactorResourceFactory reactorClientResourceFactory() {
        System.setProperty("reactor.netty.ioSelectCount","1");
 
        // 这里工作线程数为2-4倍都可以。看具体情况
        int ioWorkerCount = Math.max(Runtime.getRuntime().availableProcessors()*3, 4));
        System.setProperty("reactor.netty.ioWorkerCount",String.valueOf(ioWorkerCount);
        return new ReactorResourceFactory();
    }
}

这里版本是reactor-netty-core-1.0.3,版本不一样的话 可能参数key不太一样。可以看一下LoopResources 中写的key。

Runtime.getRuntime().availableProcessors()获取的是cpu核心线程数也就是计算资源,而不是CPU物理核心数,对于支持超线程的CPU来说,单个物理处理器相当于拥有两个逻辑处理器,能够同时执行两个线程。

源码分析

package reactor.netty.resources;
 
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import java.time.Duration;
import java.util.Objects;
import reactor.core.Disposable;
import reactor.core.publisher.Mono;
 
@FunctionalInterface
public interface LoopResources extends Disposable {
    // 这里是worker线程数,未配置的话。从cpu核心数和4直接取一个大的
    int DEFAULT_IO_WORKER_COUNT = Integer.parseInt(System.getProperty("reactor.netty.ioWorkerCount", "" + Math.max(Runtime.getRuntime().availableProcessors(), 4)));
    // 这里是select线程数 默认是-1
    int DEFAULT_IO_SELECT_COUNT = Integer.parseInt(System.getProperty("reactor.netty.ioSelectCount", "-1"));
    ....
 
    // 创建一个默认的资源,把两个线程数的参数传递过去
    static LoopResources create(String prefix) {
        if (Objects.requireNonNull(prefix, "prefix").isEmpty()) {
            throw new IllegalArgumentException("Cannot use empty prefix");
        }
        return new DefaultLoopResources(prefix, DEFAULT_IO_SELECT_COUNT, DEFAULT_IO_WORKER_COUNT, true);
    }
    ....
 

接下来看一下 DefaultLoopResources 做了什么

DefaultLoopResources(String prefix, int selectCount, int workerCount, boolean daemon) {
        this.running = new AtomicBoolean(true);
        this.daemon = daemon;
        this.workerCount = workerCount;
        this.prefix = prefix;
 
        this.serverLoops = new AtomicReference<>();
        this.clientLoops = new AtomicReference<>();
 
        this.cacheNativeClientLoops = new AtomicReference<>();
        this.cacheNativeServerLoops = new AtomicReference<>();
        // 因为默认没有配置 所以selectCode必然是-1
        if (selectCount == -1) {
            this.selectCount = workerCount;
            // serverSelectLoops没有创建,而是直接使用的serverLoops
            this.serverSelectLoops = this.serverLoops;
            this.cacheNativeSelectLoops = this.cacheNativeServerLoops;
        }
        else {
            this.selectCount = selectCount;
            this.serverSelectLoops = new AtomicReference<>();
            this.cacheNativeSelectLoops = new AtomicReference<>();
        }
    }
 
    @SuppressWarnings("FutureReturnValueIgnored")
    EventLoopGroup cacheNioSelectLoops() {
        // 两个相等的话 使用 cacheNioServerLoops 返回工作组
        if (serverSelectLoops == serverLoops) {
            return cacheNioServerLoops();
        }
 
        EventLoopGroup eventLoopGroup = serverSelectLoops.get();
        if (null == eventLoopGroup) {
            EventLoopGroup newEventLoopGroup = new NioEventLoopGroup(selectCount,
                    threadFactory(this, "select-nio"));
            if (!serverSelectLoops.compareAndSet(null, newEventLoopGroup)) {
                //"FutureReturnValueIgnored" this is deliberate
                newEventLoopGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
            }
            eventLoopGroup = cacheNioSelectLoops();
        }
        return eventLoopGroup;
    }
 
    // 这里相当于返回了工作组
    @SuppressWarnings("FutureReturnValueIgnored")
    EventLoopGroup cacheNioServerLoops() {
        EventLoopGroup eventLoopGroup = serverLoops.get();
        if (null == eventLoopGroup) {
            EventLoopGroup newEventLoopGroup = new NioEventLoopGroup(workerCount,
                    threadFactory(this, "nio"));
            if (!serverLoops.compareAndSet(null, newEventLoopGroup)) {
                //"FutureReturnValueIgnored" this is deliberate
                newEventLoopGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
            }
            eventLoopGroup = cacheNioServerLoops();
        }
        return eventLoopGroup;
    }

可以看出来,如果未配置。netty是没有select线程组的。结合分析reactor模型可以发现,这种情况对处理效率是有影响的。而且最大只和cpu核心数量相同的配置也明显无法重复利硬件用资源。


接口耗时日志

LoginPostFilter

/**
 * 打印请求参数及统计执行时长过滤器
 * @author xujiahui
 */
@Component
public class LoggingFilter implements GlobalFilter, Ordered {

    private static final Log logger = LogFactory.getLog(LoggingFilter.class);
    private static final String START_TIME = "startTime";

    public LoggingFilter() {
        logger.info("Loaded GlobalFilter [Logging]");
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String info = String.format("Method:{%s} Host:{%s} Path:{%s} Query:{%s}",
                exchange.getRequest().getMethod().name(),
                exchange.getRequest().getURI().getHost(),
                exchange.getRequest().getURI().getPath(),
                exchange.getRequest().getQueryParams());

        logger.info(info);

        exchange.getAttributes().put(START_TIME, System.currentTimeMillis());
        return chain.filter(exchange).then( Mono.fromRunnable(() -> {
            Long startTime = exchange.getAttribute(START_TIME);
            if (startTime != null) {
                Long executeTime = (System.currentTimeMillis() - startTime);
                logger.info(exchange.getRequest().getURI().getRawPath() + " : " + executeTime + "ms");
            }
        }));
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

结果

2018-08-31 18:35:02.644  INFO 14376 --- [ctor-http-nio-2] c.w.gateway.filter.LoggingFilter       : Method:{POST} Host:{127.0.0.1} Path:{/api/authorize} Query:{{}}
2018-08-31 18:35:02.678  INFO 14376 --- [ctor-http-nio-3] c.w.gateway.filter.LoggingFilter       : /api/authorize : 34ms

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

3 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 594109浏览

随机文章
Java—数据库连接池
3年前
Spring—@ControllerAdvice等异常处理方式或统一处理数据
5年前
Spring—事务总结
3年前
SpringBoot—Redis缓存(Cache)
5年前
Netty—初探与核心(未完)
2年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 594109 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付