SpringCloud Gateway的路由,过滤器和限流解读
目录
- Spring Cloud Gateway
- predicates路由断言工厂
- 全局过滤器
- fGatewayFilter工厂
- filters配置
- Hystrix GatewayFilter工厂
- 限流RequestRateLimiter GatewayFilter工厂
- 参考文档
- 总结
Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?
Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets,Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性。
Spring Cloud Gateway
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
注意:
Spring Cloud Gateway是基于Spring Boot 2.x, Spring WebFlux和Projec开发者_JAVA开发t Reactor 构建的。
因此,在使用Spring Cloud Gateway时,许多不熟悉的同步库(例如,Spring Data和Spring Security)和模式可能不适用。
如果您对这些项目不熟悉,建议您在使用Spring Cloud Gateway之前先阅读它们的文档,以熟悉一些新概念。
Spring Cloud Gateway需要Spring Boot和Spring Webflux提供的Netty运行时。它不能在传统的Servlet容器中或作为WAR构建。
Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
Spring Cloud Gateway 的特征:
- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
- 动态路由
- Predicates 和 Filters 作用于特定路由
- 集成 Hystrix 断路器
- 集成 Spring Cloud DiscoveryClient
- 易于编写的 Predicates 和 Filters
- 限流
- 路径重写
相关概念:
- Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
- Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
- Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。
Spring Cloud Gateway 网关路由有两种配置方式:
- 在配置文件 yml 中配置通过@Bean自定义 RouteLocator,在启动主类 Application 中配置
- 这两种方式是等价的,建议使用 yml 方式进配置。
pom.XML
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> http://www.devze.com <version>Finchley.SR2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies>
application.yml
server: port: 8080 spring: cloud: gateway: routes: - id: bamboo_route uri: https://blog.csdn.net/ predicates: - Path=/blogdevteam
yml方式配置路由
id
:我们自定义的路由 ID,保持唯一uri
:目标服务地址predicates
:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。filters
:过滤规则,本示例暂时没用。
启动类:代码方式配置路由
package com.bamboo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; /** * @program: spring-gateway * @description: Application * @author: Bamboo zjcjava@163.com * @create: 2019-10-26 20:00 **/ @SpringBootApplication public class Application { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() // basic proxy .route(r -> r.path("/zjcjava") .uri("https://blog.csdn.net//")) .build(); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
http://localhost:8080/zjcjava
对应访问的地址是https://blog.csdn.net/zjcjavahttp://localhost:8080/blogdevteam
对应访问的地址是https://blog.csdn.net/blogdevteam
可以看出来,它会自动在url路径/后面加上对应的路由地址
predicates路由断言工厂
Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping基础架构的一部分进行匹配。Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些谓词都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以合并,也可以通过逻辑合并and。
Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。
spring: cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true ##会使用serviceId转发到具体的服务IP上的服务 routes: - id: after_route uri: https://example.org predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] - Before=2017-01-20T17:42:47.789-07:00[America/Denver] - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] - Cookie=chocolate, ch.p - Header=X-Request-Id, \d+ - Host=**.somehost.org,**.anotherhost.org - Method=GET - Path=/foo/{segment},/bar/{segment} - Query=baz - RemoteAddr=192.168.1.1/24 - id: weight_high uri: https://weighthigh.org predicates: - Weight=group1, 8 - id: weight_low uri: https://weightlow.org predicates: - Weight=group1, 2
spring.cloud.gateway.discovery.locator.enabled:使用eurake注册服务后:是否与服务发现组件ID进行结合,通过routes中的serviceId 转发python到具体的服务实例。
默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。
- 后路线断言工厂:After=2017年1月20日17:42山区时间(丹佛)之后的所有请求匹配
- 路线断言工厂之前:Before=2017年1月20日17:42山区时间(丹佛)之前的所有请求匹配。
- 路线断言工厂之间- Between=2017年1月20日山区时间(丹佛)之后和2017年1月21日17:42山区时间(丹佛)之后的所有请求匹配
- Cookie路线断言工厂 Cookie=请求匹配一个名为chocolatewho的值为ch.p正则表达式匹配的cookie
- 标头路由断言工厂 - Header=匹配请求具有名为X-Request-Id其值为\d+正则表达式匹配(具有一个或多个数字的值)的标头
- 主机路由断言工厂 - Host=请求的Host标头中包含值www.somehost.org或beta.somehost.org,则此路由将匹配www.anotherhost.org
- 方法路线断言工厂 - Method=此路由将匹配GET方式的请求
- 路径路线断言工厂 - Path=将匹配:/foo/1或/foo/bar或/bar/baz
- 查询路由断言工厂 - Query=匹配请求包含baz查询参数
- RemoteAddr路由断言工厂 - RemoteAddr=请求的远程地址为192.168.1.1/24之间的IP,则此路由将匹配192.168.1.10
- 权重路线断言工厂 将约80%的流量转发至weighthigh.org,并将约20%的流量转发至weightlow.org
全局过滤器
该GlobalFilter接口具有与相同的签名GatewayFilter。这些是特殊过滤器,有条件地应用于所有路由。(此界面和用法可能会在将来的里程碑中更改)。
全局过滤器和GatewayFilter的组合订购
当请求进入(并与路由匹配)时,过滤Web处理程序会将的所有实例GlobalFilter和所有特定GatewayFilter于路由的实例添加到过滤器链中。该组合的过滤器链按org.springframework.core.Ordered接口排序,可以通过实现该getOrder()方法进行设置。
由于Spring Cloud Gateway区分了执行过滤器逻辑的“前”阶段和“后”阶段(请参阅:工作原理),因此优先级最高的过滤器将在“前”阶段中处于第一个阶段,而在“后”阶段中处于最后一个阶段“-相。
ExampleConfiguration.java
//放入application启动类中main方法后面即可 @Bean public CustomGlobalFilter tokenFilter() { return new CustomGlobalFilter(); } /** * @program: spring-gateway * @description: 全局过滤器 * @author: Bamboo zjcjava@163.com * @create: 2019-10-26 23:06 **/ public class CustomGlobalFilter implements GlobalFilter, Ordered { private static final Logger log = LoggerFactory.getLogger(CustomGlobalFilter.class); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("custom global filter....................................."); // 添加全局鉴权 String token = exchange.getRequest().getQueryParams().getFirst("token"); if (token == null || token.isEmpty()) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return -1; } }
这里我创建了一个全局的鉴权过滤器,只有参数中带有token值才能继续,否则提示无权访问
http://localhost:8080/zjcjava?token=aa
fGatewayFilter工厂
路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器适用于特定路由。Spring Cloud Gateway包括许多内置的GatewayFilter工厂。
filters配置
修改配置文件如下:抓哟是 path,filters
server: port: 8080 spring: application: name: spring-cloud-gateway cloud: gateway: routes: - id: bamboo_route uri: https://blog.csdn.net/ predicates: # - Path=/blogdevteam - Path=/test/blogdevteam filters: - StripPrefix=1 #去掉前缀 - AddResponseHeader=X-Response-Default-Foo, Default-Bar #返回消息头添加head信息
StripPrefix=1 #去掉第1个前缀以/分割
AddResponseHeader返回报文消息头添加head信息
这里只列举几个重要的过滤器
Hystrix GatewayFilter工厂
Hystrix是Netflix的一个库,用于实现断路器模式。Hystrix GatewayFilter允许您将断路器引入网关路由,保护您的服务免受级联故障的影响,并允许您在下游故障的情况下提供后备响应。
要在项目中启用Hystrix GatewayFilters,请spring-cloud-starter-netflix-hystrix从Spring Cloud Netflix添加依赖项。
Hystrix GatewayFilter工厂需要一个name参数,它是的名称HypythonstrixCommand。
spring: cloud: gateway: routes: - id: hystrix_route uri: https://example.org filters: - Hystrix=myCommandName
这会将其余的过滤器包装在HystrixCommand带有命令名的中myCommandName。
Hystrix过滤器还可以接受可选fallbackUri参数。当前,仅forward:支持计划的URI。如果调用了后备,则请求将被转发到与URI相匹配的控制器。
限流RequestRateLimiter GatewayFilter工厂
RequestRateLimiter GatewayFilter Factory使用一种RateLimiter实现来确定是否允许继续当前请求。如果不是,HTTP 429 - Too Many Requests则返回状态(默认)。
此过滤器采用一个可选keyResolver参数和特定于速率限制器的参数(请参见下文)。
keyResolver是实现KeyResolver接口的bean 。在配置中,使用SpEL按名称引用bean。#{@myKeyResolver}是SpEL表达式,它引用名称为的bean myKeyResolver。
Redis RateLimiter
redis实现基于Stripe所做的工作。它需要使用spring-boot-starter-data-redis-reactiveSpring Boot启动器。
使用的算法是令牌桶算法。
该redis-rate-limiter.replenishRate是多么的每秒许多请求你希望用户被允许做,没有任何下降的请求。这是令牌桶被填充的速率。
redis-rate-limiter.burstCapacity是允许用户在一个单一的第二做请求的最大数目。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求。
通过在replenishRate和中设置相同的值,可以达到稳定的速率burstCapacity。设置burstCapacity大于可以允许临时爆发replenishRate。在这种情况下,速率限制器需要在突发之间间隔一段时间(根据replenishRate),因为2个连续的突发将导致请求丢失(HTTP 429 - Too Many Requests)。
application.yml
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
配置文件
@Bean KeyResolver userKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); }
这定义了每个用户10的请求速率限制。允许20个并发,但是下一秒只有10个请求可用。这KeyResolver是一个简单的获取user请求参数的参数(注意:不建议在生产中使用)。
速率限制器也可以定义为实现RateLimiter接口的Bean 。在配置中,使用SpEL按名称引用bean。#{@myRateLimiter}是SpEL表达式,它引用名称为的bean myRateLimiter。
application.yml
spring: cloud: gateway: routes: - id: requestratelimiter_route www.devze.com uri: https://example.org filters: - name: RequestRateLimiter args: rate-limiter: "#{@myRateLimiter}" key-resolver: "#{@userKeyResolverWkvbF}"
参考文档
官方参考文档:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.0.RC1/reference/html/
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。
精彩评论