轻量级前端框架助力开发者提升项目效率与性能
1877
2022-10-08
【云原生&微服务>SCG网关篇七】Spring Cloud Gateway基于内置Filter实现限流、熔断、重试
文章目录
一、前言二、结合Redis实现限流(RequestRateLimiterGatewayFilterFactory)
1、不指定KeyResolver的限流2、指定KeyResolver的限流
三、熔断
1、SpringCloudCircuitBreakerFilterFactory
1)针对所有的请求断路
指定断路后的fallbackURI(gateway内部)指定断路后的fallbackURI(gateway外部)
2)针对返回的状态码 断路
2、FallbackHeadersGatewayFilterFactory
四、重试(RetryGatewayFilterFactory)五、总结
一、前言
至此微服务网关系列文章已出:
【云原生&微服务>SCG网关篇一】为什么要有网关、生产环境如何选择网关云原生&微服务>SCG网关篇二】生产上那些灰度发布方式【云原生&微服务>SCG网关篇三】Spring Cloud Gateway是什么、详细使用案例云原生&微服务>SCG网关篇四】Spring Cloud Gateway内置的11种PredicateFactory如何使用【云原生&微服务>SCG网关篇五】Spring Cloud Gateway自定义PredicateFactory【云原生&微服务>SCG网关篇六】Spring Cloud Gateway内置的18种Filter使用姿势
聊了以下问题:
为什么要有网关?网关的作用是什么?网关的分类?网关的技术选型?使用网关时常用的灰度发布方式有哪些?Spring Cloud Gateway是什么?详细使用案例?Spring Cloud Gateway内置的11种PredicateFactory如何自定义PredicateFactory?Spring Cloud Gateway内置的18种常用的Filter
本文接着聊Spring Cloud Gateway基于内置Filter如何实现限流、熔断、重试。
PS:SpringCloud版本信息:
二、结合Redis实现限流(RequestRateLimiterGatewayFilterFactory)
Spring Cloud Gateway可以使用RequestRateLimiter GatewayFilter factory结合Redis基于令牌桶算法实现限流功能;如果请求无法通过限流,则会返回HTTP 429 - Too Many Requests;
RequestRateLimiter 采用可选的keyResolver参数和特定于速率限制器的参数;
keyResolver参数是实现KeyResolver接口的Bean,KeyResolver接口让 可插入策略 派生出限制请求的密钥; KeyResolver的默认实现是PrincipalNameKeyResolver,它从ServerWebExchange中检索出Principal并调用Principal.getName()。
结合Redis实现限流需要在gateway项目中引入spring-boot-starter-data-redis-reactive的Spring Boot starter,因为Redis的实现基于Stripe工作的。
否者会报错:
RequestRateLimiter速率限制器的参数如下:
redis-rate-limiter.replenishRate,在不丢弃请求的情况下,允许用户每秒执行的请求数,即:每秒令牌桶的填充速率。redis-rate-limiter.burstCapacity,表示允许用户在一秒钟内执行的最大请求数,即:令牌桶可以容纳的令牌数;将此值设置为零会阻止所有请求。redis-rate-limiter.requestedTokens,表示一个请求需要多少个令牌,即:针对每个请求从令牌桶提取的令牌数,默认为1。
通过设置redis-rate-limiter.replenishRate 和 redis-rate-limiter.burstCapacity相等可以实现稳定限流速率。
如果将redis-rate-limiter.burstCapacity设置为高于 redis-rate-limiter.replenishRate,则允许临时的挤满 / 冲突。
这种情况下,速率限制器(rate limiter)会在两次挤满 / 突发之间留出一段时间(根据令牌桶填充速率决定速率,比如:填充速率是5,令牌最大数是10,则富余5个令牌,可以临时顶1s),因为两次连续的挤满 / 冲突发会导致请求丢失(HTTP 429 - Too Many Requests)。
1、不指定KeyResolver的限流
结合Redis实现的限流,当不指定KeyResolver参数时,会采用KeyResolver的默认实现,对所有请求进行限流;
示例:
redis: host: 127.0.0.1 port: 6379server: port: 9999spring: cloud: gateway: routes: - id: add_request_parameter_route uri: predicates: - Path=/** filters: # 请求数限流 名字不能随便写 ,使用默认的factory,默认使用令牌桶算法 - name: RequestRateLimiter args: # 针对请求IP进行限流# key-resolver: "#{@ipKeyResolver}" # 在不丢弃请求的情况下,允许用户每秒执行的请求数,即令牌桶填充速率:1/s(一秒放一个) redis-rate-limiter.replenishRate: 1 # 表示允许用户在一秒钟内执行的最大请求数,即:令牌桶可以容纳的令牌数;将此值设置为零会阻止所有请求 redis-rate-limiter.burstCapacity: 1 # 表示一个请求需要多少个令牌,默认为1 redis-rate-limiter.requestedTokens: 1
这里表示对所有的请求,每秒仅允许一个请求过去,其余的请求都会被拦截,报错:HTTP 429 - Too Many Requests;
2、指定KeyResolver的限流
KeyResolver接口主要用于设置限流请求的key;我们可以通过指定KeyResolver将请求进行分批限流,比如针对同一个IP的请求做限流;
1> 传建一个KeyResolver接口的实现类:IpKeyResolver
package com.saint.gateway.filter;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;/** * KeyResolver接口主要用于设置限流请求的key,通过实现该接口指定需要对当前请求中对哪些因素进行流量控制。 */@Component("ipKeyResolver")public class IpKeyResolver implements KeyResolver { @Override public Mono
2> 在yaml中配置KeyResolver
redis: host: 127.0.0.1 port: 6379server: port: 9999spring: cloud: gateway: routes: - id: add_request_parameter_route uri: predicates: - Path=/**# - Path=/gateway/** filters: # 请求数限流 名字不能随便写 ,使用默认的factory,默认使用令牌桶算法 - name: RequestRateLimiter args: # 针对请求IP进行限流 key-resolver: "#{@ipKeyResolver}" # 在不丢弃请求的情况下,允许用户每秒执行的请求数,即令牌桶填充速率:1/s(一秒放一个) redis-rate-limiter.replenishRate: 1 # 表示允许用户在一秒钟内执行的最大请求数,即:令牌桶可以容纳的令牌数;将此值设置为零会阻止所有请求 redis-rate-limiter.burstCapacity: 1 # 表示一个请求需要多少个令牌,默认为1 redis-rate-limiter.requestedTokens: 1
三、熔断
1、SpringCloudCircuitBreakerFilterFactory
Spring Cloud Gateway可以使用CircuitBreaker GatewayFilter factory基于Spring Cloud CircuitBreaker APIs在断路器中包裹网关路由;Spring Cloud CircuitBreaker中存在很多支持Spring Cloud Gateway使用的库,比如:开箱即用的Resilience4J。
和RequestRateLimiter结合Redis实现限流需要引入spring-boot-starter-data-redis-reactive一样,CircuitBreaker需要引入spring-cloud-starter-circuitbreaker-reactor-resilience4j。
关于Spring Cloud CircuitBreaker,参考官方文章:自定义一个ReactiveResilience4JCircuitBreakerFactory:
package com.saint.gateway.config;import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;import io.github.resilience4j.timelimiter.TimeLimiterConfig;import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory;import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;import org.springframework.cloud.client.circuitbreaker.Customizer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.time.Duration;/** * @author Saint */@Configurationpublic class GatewayConfiguration { @Bean(name = "myCircuitBreaker") public Customizer
断路器CircuitBreaker中配置了路由的超时时间为3s,默认为1s。
2> 在application.yml中指定过滤器Filter:
spring: cloud: gateway: routes: - id: add_request_parameter_route uri: predicates: - Path=/** filters: -
访问Spring Cloud Gateway详细使用案例中的/hello/timeout接口,其中线程睡眠了5s,所以一定会超时。
到这里超时的场景已经验证完毕,再修改CircuitBreaker的配置,将超时时间修改为6s,再访问一下接口:
已经可以正常响应。
当断路器生效、断路之后,我们想自定义它返回的信息,可以通过制定断路后的fallbackURI实现。
指定断路后的fallbackURI(gateway内部)
Spring Cloud CircuitBreaker filter可以指定一个可选择的参数fallbackUri,用于指定断路后重定向的URI。重定向的URI可以是Gateway项目内部的Controller、Route,也可以是外部的链接。
1> 在Gateway项目中新增一个Controller:
package com.saint.gateway.controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;import java.util.Map;@RestControllerpublic class FallbackController { @RequestMapping("/defaultFallback") public Map defaultFallback() { Map map = new HashMap<>(); map.put("code", 999); map.put("message", "server error"); return map; }}
2> 在application.yml文件中指定fallbackURI:
server: port: 9999spring: cloud: gateway: routes: - id: add_request_parameter_route uri: predicates: - Path=/** filters: - name: CircuitBreaker args: name: myCircuitBreaker # 指定断路后的重定向地址 fallbackUri: forward:/defaultFallback
当断路器断路后,会将请求重定向到当前Gateway服务的/defaultFallback(Body也变成了相应重定向后地址的响应体。
指定断路后的fallbackURI(gateway外部)
我们也可以通过将外部地址配置到Route的方式,进而fallback到路由上,实现短路后fallback重定向到外部链接的需求。
server: port: 9999spring: cloud: gateway: routes: - id: ingredients-fallback uri: predicates: - Path=/fallback # 通过过滤器将地址重写为:/hello/sayParam filters: - SetPath=/hello/sayParam - id: add_request_parameter_route uri: predicates: - Path=/** filters: - name: CircuitBreaker args: name: myCircuitBreaker # 指定断路后的重定向地址(route的方式) fallbackUri: forward:/fallback
示例中将外部地址断路
在某些情况下,我们可能希望根据Route返回的状态码使断路器跳闸;断路器配置对象拥有一个状态码的集合,如果路由返回的状态码在集合中,则断路器跳闸;状态码可以使用代表码值的整数 或 HttpStatus枚举的字符串。
server: port: 9999spring: cloud: gateway: routes: - id: add_request_parameter_route uri: predicates: - Path=/** filters: - name: CircuitBreaker args: name: myCircuitBreaker # 指定断路后的重定向地址(gateway内部controller) fallbackUri: forward:/defaultFallback # 当且仅当出现如下状态码时,断路器才会生效 statusCodes: - 500 - "NOT_FOUND"
当路由返回的状态码为500、或404(“NOT_FOUND”)时会触发断路器,其余状态码均不会。
/hello/retryRoute接口会中会直接报错,返回500,断路器将500的状态码进行了拦截。
如果不配置statusCodes,那么针对/hello/retryRoute接口的熔断器不会生效,依旧是直接报错。
2、FallbackHeadersGatewayFilterFactory
FallbackHeaders factory 可以将一些Spring Cloud CircuitBreaker执行的异常细节添加到fallbackUri的请求头中,请求头中的属性可以包括:
executionExceptionTypeHeaderName (“Execution-Exception-Type”)executionExceptionMessageHeaderName (“Execution-Exception-Message”)rootCauseExceptionTypeHeaderName (“Root-Cause-Exception-Type”)rootCauseExceptionMessageHeaderName (“Root-Cause-Exception-Message”)
spring: cloud: gateway: routes: - id: ingredients-fallback uri: predicates: - Path=/fallback # 通过过滤器将地址重写为:/hello/sayParam filters: - SetPath=/hello/sayParam - id: add_request_parameter_route uri: predicates: - Path=/** filters: - name: CircuitBreaker args: name: myCircuitBreaker # 指定断路后的重定向地址(route的方式) fallbackUri: forward:/fallback # 当且仅当出现如下状态码时,断路器才会生效 statusCodes: - 500 - "NOT_FOUND" - name: FallbackHeaders args: executionExceptionTypeHeaderName: Saint-Test-Header
访问:fateway-center项目,可以发现请求进了FallbackHeadersGatewayFilterFactory,不过由于FallbackHeadersGatewayFilterFactory中无法从ServerWebExchange中获取到的circuitBreakerExecutionException属性值为null,所以没有异常信息记录到fallbackUri的请求头中;
从上图的代码逻辑也可以看出,只有当circuitBreakerExecutionException属性有值时,才会将异常信息记录到fallbackUri的请求头中。
四、重试(RetryGatewayFilterFactory)
Retry GatewayFilter factory支持以下参数:
retries: 重试次数;statuses: 可以进行重试的响应的HTTP状态码, 使用org.springframework.可以进行重试的HTTP methods, 使用org.springframework.可以进行重试的The series of status codes,使用org.springframework.可以进行重试的Thrown Exceptions 列表;backoff:为重试配置指数级的重试时间间隔;在firstBackoff *(factor ^ n)的重试间隔后执行重试,其中n是迭代(第几次重试);如果配置了maxBackoff,则重试的最大时间间隔为maxBackoff;如果basedOnPreviousValue被设置为true,则重试时间间隔使用prevBackoff * factor 计算。
spring: cloud: gateway: routes: - id: add_request_parameter_route uri: predicates: - Path=/** filters: # 重试 - name: Retry args: # 请求重试次数,默认值是3 retries: 3 # 可以进行重试的状态码(500、404), statuses: INTERNAL_SERVER_ERROR,NOT_FOUND # 可以进行重试的Http Method methods: GET,POST # 重试的时间间隔配置 backoff: # 第一次重试的时间间隔 firstBackoff: 100ms # 最大重试时间间隔 maxBackoff: 500ms # 时间间隔因子 factor: 2 # 关闭根据上次重试时间间隔计算当前重试时间间隔功能 basedOnPreviousValue: false
示例效果:
请求地址:Cloud Gateway基于RequestRateLimiterGatewayFilterFactory实现限流、基于SpringCloudCircuitBreakerFilterFactory实现熔断、基于RetryGatewayFilterFactory实现重试;
另外本文相关案例全部来自:【云原生&微服务>SCG网关篇三】Spring Cloud Gateway是什么、详细使用案例。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~