springcloud gateway 实现服务转发 2

新手学堂016

springcloud gateway 实现服务转发 2,第1张

gateway转发配置常见的有两种方式,一种是 yml配置的方式:

参考:springcloud gateway 服务转发

本文介绍另一种方式,即 配置类的方式实现,本质没啥区别,写法不同而已。

只需要在项目包扫描路径下建一个配置类即可,如下:

上面的例子分别定义了,路由的id,匹配转发的路径和对应的服务。

当请求到达网关以后,遇到test-app1/的路径时,请求会转发到test-app1的服务。

需要注意的是,由于gateway是基于webflux的,不同于springMVC,所以pom文件需要将

web的包排除掉,否则会无法启动应用。

请求分发处理器,是WebFlux的访问入口。看这似曾相识的样子,没错,对应到Spring MVC中,跟它承担类似作用的,就是DispatcherServlet。DispatcherHander也和DispatcherServlet的请求处理流程类似:

执行他的核心方法:

她的本职工作是将GlobalFilter(全局过滤器)、GatewayFilter(一般来说是我们自己配置的,当然也默认内置了一些,也内含了自己在application配置文件中配的defaultFilter[如果有陪置的话]),放到同一个List里进行优先级排序,生成一个过滤器链。执行过滤器链,就能顺序执行链中保存的所有Filter。

参考文章:

spring cloud gateway 专题收录1

spring cloud gateway 专题收录2

1 由于 spring cloud gateway 是基于 WebFlux 框架实现的,该网关作为资源服务器时不能使用 @EnableResourceServer 注解,需要使用 @EnableWebFluxSecurity 注解来配置安全过滤链。

2 在 springboot 22 之前的版本中,安全框架对应的是 spring-security 514,该版本只实现了基于 id token (jwk) 的认证,而我当前项目中的认证服务组件是基于 orgspringframeworkcloud:spring-cloud-starter-oauth2 框架开发,使用的是秘钥签名的 access token,所以网关服务组件需要使用 springboot 22 + spring security 52 来处理 jws。

3 现有项目使用了 gradle 构建,是一个多模块的结构,其中主模块引入了 212RELEASE 版本的 orgspringframeworkboot 插件,用来确保各模块中 spring 组件的版本统一,此时子模块是无法通过修改插件版本号或重新引入插件来改变模块中 springboot 的版本,所以网关模块想用要引入 springboot 22 的话,就得脱离主模块,或者将插件引入的操作直接下放到各个子模块的构建过程中。

4 orgspringframeworkcloud:spring-cloud-starter-oauth2 中 orgspringframeworksecurityjwtcryptosignMacSigner 支持使用短密码的 HMACSHA256 签名算法,NimbusReactiveJwtDecoder 不支持短密码。

RouteDefinition路由定义,Spring-Cloud-Gateway通过RouteDefinition来转换生成具体的路由信息。RouteDefinition的信息是怎么加载初始化到网关系统中的,接下来阅读Spring-Cloud-Gateway的RouteDefinitionLocator(路由定义信息加载器)接口,学习RouteDefinition初始化加载

首先查看RouteDefinitionLocator源码

RouteDefinitionLocator接口有且仅有一个方法getRouteDefinitions,此方法获取RouteDefinition的核心方法,返回Flux<RouteDefinition>

RouteDefinitionLocator 类图如下:

子类功能描述:

通过子类实现具体功能可以很清晰的看到定位器加载RouteDefinition整个流程

最终提供通过CompositeRouteDefinitionLocator提供统一的

getRouteDefinitions方法

RouteDefinitionLocator实例的初始化在GatewayAutoConfiguration中已经完成

GatewayDiscoveryClientAutoConfiguration

GatewayAutoConfiguration

在Spring-Cloud-Gateway初始化完成后需要的路由定义加载器已全部实例化完成,这样就为路由的加载创建完成了必要的条件。

接 上篇

RequestRateLimiter GatewayFilter Factory使用 RateLimiter 来决定当前请求是否允许通过,如果不允许,则默认返回状态码 HTTP 429 - Too Many Requests 。

RequestRateLimiter GatewayFilter可以使用一个可选参数 keyResolver 来做速率限制。

keyResolver 是 KeyResolver 接口的一个实现bean,在配置里面,通过SpEL表达式 #{@myKeyResolver} 来管理bean的名字 myKeyResolver 。

KeyResolverjava

KeyResolver 接口允许你使用不同的策略来得出限制请求的key,未来,官方也会推出一些 KeyResolver 的不同实现。

KeyResolver 默认实现是 PrincipalNameKeyResolver ,通过 ServerWebExchange 中获取 Principal ,并以 PrincipalgetName() 作为限流的key。

如果 KeyResolver 拿不到key,请求默认都会被限制,你也可以自己配置 springcloudgatewayfilterrequest-rate-limiterdeny-empty-key :是否允许空key, springcloudgatewayfilterrequest-rate-limiterempty-key-status-code :空key时返回的状态码。

applicationproperties

基于 Stripe 的redis实现方案,依赖 spring-boot-starter-data-redis-reactive Spring Boot starter,使用的是令牌桶算法。

redis-rate-limiterreplenishRate 配置的是每秒允许通过的请求数,其实就是令牌桶的填充速率。

redis-rate-limiterburstCapacity 配置的是一秒内最大的请求数,其实就是令牌桶的最大容量,如果设置为0,则会阻塞所有请求。

所以可以通过设置相同的 replenishRate 和 burstCapacity 来实现匀速的速率控制,通过设置 burstCapacity 大于 replenishRate 来允许系统流量瞬间突发,但是对于这种情况,突发周期为 burstCapacity / replenishRate 秒,如果周期内有两次请求突发的情况,则第二次会有部分请求丢失,返回 HTTP 429 - Too Many Requests 。

applicationyml

Configjava

上面定义了每个用户每秒10个请求的速率限制,允许20的突发流量,突发完,下一秒只允许10个请求通过了, KeyResolver 定义了通过请求获取请求参数 user 作为key。

你也可以实现 RateLimiter 接口自定义自己的请求速率限制器,在配置文件中使用SpEL表达式配置对应的bean的名字即可。

applicationyml

RedirectTo GatewayFilter Factory使用 status 和 url 两个参数,其中 status 必须是300系列的HTTP状态码, url 则是跳转的地址,会放在响应的 Location 的header中(http协议中转跳的header)。

applicationyml

上面路由会执行302重定向到 http://wwwedjdhbbcom 。

RemoveNonProxyHeaders GatewayFilter Factory转发请求是会根据 IETF 的定义,默认会移除下列的http头信息:

你也可以通过配置 springcloudgatewayfilterremove-non-proxy-headersheaders 来更改需要移除的header列表。

RemoveRequestHeader GatewayFilter Factory配置header的name,即可以移除请求的header。

applicationyml

上面路由在发送请求给下游时,会将请求中的 X-Request-Foo 头信息去掉。

RemoveResponseHeader GatewayFilter Factory通过配置header的name,会在响应返回时移除header。

applicationyml

上面路由会在响应返回给gateway的客户端时,将 X-Response-Foo 响应头信息去掉。

RewritePath GatewayFilter Factory使用路径 regexp 和替换路径 replacement 两个参数做路径重写,两个都可以灵活地使用java的正则表达式。

applicationyml

对于上面的例子,如果请求的路径是 /foo/bar ,则gateway会将请求路径改为 /bar 发送给下游。

RewriteResponseHeader GatewayFilter Factory的作用是修改响应返回的header内容,需要配置响应返回的header的 name ,匹配规则 regexp 和替换词 replacement ,也是支持java的正则表达式。

applicationyml

举个例子,对于上面的filter,如果响应的header X-Response-Foo 的内容是 /42?user=ford&password=omg!what&flag=true ,这个内容会修改为 /42user=ford&password=&flag=true 。

SaveSession GatewayFilter Factory会在请求下游时强制执行 WebSession::save 方法,用在那种像 Spring Session 延迟数据存储的,并在请求转发前确保session状态保存情况。

applicationyml

如果你将 Spring Secutiry 于 Spring Session 集成使用,并想确保安全信息都传到下游机器,你就需要配置这个filter。

SecureHeaders GatewayFilter Factory会添加在返回响应中一系列安全作用的header,至于为什么,英文好的可以看一下 这篇博客 。

默认会添加这些头信息和默认内容:

如果你想修改这些头信息的默认内容,可以在配置文件中添加下面的配置:

前缀: springcloudgatewayfiltersecure-headers

上面的header对应的后缀:

前后缀接起来即可,如: springcloudgatewayfiltersecure-headersxss-protection-header

SetPath GatewayFilter Factory采用路径 template 参数,通过请求路径的片段的模板化,来达到操作修改路径的母的,运行多个路径片段模板化。

applicationyml

对于上面的例子,如果路径是 /foo/bar ,则对于下游的请求路径会修改为 /bar 。

SetResponseHeader GatewayFilter Factory通过设置 name 和 value 来替换响应对于的header。

applicationyml

对于上面的例子,如果下游的返回带有头信息为 X-Response-Foo:1234 ,则会gateway会替换为 X-Response-Foo:Bar ,在返回给客户端。

SetStatus GatewayFilter Factory通过配置有效的Spring HttpStatus 枚举参数,可以是类似于404的这些数字,也可以是枚举的name字符串,来修改响应的返回码。

applicationyml

上面例子中,两种路由都会将响应的状态码设置为401。

StripPrefix GatewayFilter Factory通过配置 parts 来表示截断路径前缀的数量。

applicationyml

如上面例子中,如果请求的路径为 /name/bar/foo ,则路径会修改为 /foo ,即将路径的两个前缀去掉了。

Retry GatewayFilter Factory可以配置针对不同的响应做请求重试,可以配置如下参数:

applicationyml

上面例子,当下游服务返回502状态码时,gateway会重试3次。

RequestSize GatewayFilter Factory会限制客户端请求包的大小,通过参数 RequestSize 来配置最大上传大小,单位字节。

applicationyml

如果请求大小超过5000kb限制,则会返回状态码 413 Payload Too Large 。

Modify Request Body GatewayFilter Factory可以修改请求体内容,这个只能通过java来配置。

Modify Response Body GatewayFilter Factory用于修改响应返回的内容,同样只能通过java配置。

这一章接着上一章介绍了Spring Cloud Gateway官方的Gateway Filter使用场景,下一章讲 Global Filters的使用 。

如果想查看其他spring cloud gateway的案例和使用,可以点击 查看

最近测试同学对系统进行压测。报出一个问题:几乎所有接口的成绩都不太好。甚至一些仅仅是主键查询,并且数据量不大的接口也是如此。排查过程中:跳过gateway网关,直接通过目标服务器ip进行压测发现成绩提升明显。初步判断是网关问题。网上翻阅资料发现一个优化点,就是netty本身的线程池配置。

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

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

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

接下来看一下 DefaultLoopResources做了什么

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