前言

目前,Spring Cloud Gateway是仅次于Spring Cloud Netflix的第二个最受欢迎的Spring Cloud项目(就GitHub上的星级而言)。它是作为Spring Cloud系列中Zuul代理的继任者而创建的。该项目提供了用于微服务体系结构的API网关,并基于反应式Netty和Project Reactor构建。它旨在提供一种简单而有效的方法来路由到API并解决诸如安全性,监视/度量和弹性之类的普遍关注的问题。

基于Redis限流

Spring Cloud Gateway为您提供了许多功能和配置选项。今天,我将集中讨论网关配置的一个非常有趣的方面-速率限制。速率限制器可以定义为一种控制网络上发送或接收的流量速率的方法。我们还可以定义几种类型的速率限制。Spring Cloud Gateway当前提供了一个Request Rate Limiter,它负责将每个用户每秒限制为N个请求。与Spring Cloud Gateway一起
使用时RequestRateLimiter,我们可能会利用Redis。Spring Cloud实现使用令牌桶算法做限速。该算法具有集中式存储桶主机,您可以在其中对每个请求获取令牌,然后将更多的令牌缓慢滴入存储桶中。如果存储桶为空,则拒绝该请求。

项目演示源码地址:https://github.com/1ssqq1lxr/SpringCloudGatewayTest

  1. 引入maven依赖

## spring cloud依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version>
</parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>11</java.version><spring-cloud.version>Hoxton.RC2</spring-cloud.version>
</properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>## gateway 依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>org.testcontainers</groupId><artifactId>mockserver</artifactId><version>1.12.3</version><scope>test</scope>
</dependency>
<dependency><groupId>org.mock-server</groupId><artifactId>mockserver-client-java</artifactId><version>3.10.8</version><scope>test</scope>
</dependency>
<dependency><groupId>com.carrotsearch</groupId><artifactId>junit-benchmarks</artifactId><version>0.7.2</version><scope>test</scope>
</dependency>
  1. 限流器配置

使用Spring Cloud Gateway默认请求限流GatewayFilter(org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter)。使用默认的Redis限流器方案,你可以通过自定义keyResolver类去决定Redis限流key的生成,下面举常用几个例子:

  • 根据用户:
    使用这种方式限流,请求路径中必须携带userId参数
@Bean
KeyResolver userKeyResolver() {return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
  • 根据获uri
@Bean
KeyResolver apiKeyResolver() {return exchange -> Mono.just(exchange.getRequest().getPath().value());
}

由于我们已经讨论了Spring Cloud Gateway速率限制的一些理论方面,因此我们可以继续进行实施。首先,让我们定义主类和非常简单的KeyResolverbean,它始终等于一个。

@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}@BeanKeyResolver userKeyResolver() {return exchange -> Mono.just("1");}
}

Gateway默认使用org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter限流器。
现在,如通过模拟Http请求,则会收到以下响应。它包括一些特定的header,其前缀为x-ratelimit。

  • x-ratelimit-burst-capacity:最大令牌值,
  • x-ratelimit-replenish-rate:填充的速率值,
  • x-ratelimit-remaining:剩下可请求数。

yaml配置:

server:port: ${PORT:8085}spring:application:name: gateway-serviceredis:host: localhostport: 6379cloud:gateway:routes:- id: account-serviceuri: http://localhost:8091predicates:- Path=/account/**filters:- RewritePath=/account/(?.*), /$\{path}- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 10redis-rate-limiter.burstCapacity: 20
  1. Redis限流器实现

关键源码如下:

// routeId也就是我们的服务路由id,id就是限流的key
public Mono<Response> isAllowed(String routeId, String id) {// 会判断RedisRateLimiter是否初始化了if (!this.initialized.get()) {throw new IllegalStateException("RedisRateLimiter is not initialized");}// 获取routeId对应的限流配置Config routeConfig = getConfig().getOrDefault(routeId, defaultConfig);if (routeConfig == null) {throw new IllegalArgumentException("No Configuration found for route " + routeId);}// 允许用户每秒做多少次请求int replenishRate = routeConfig.getReplenishRate();// 令牌桶的容量,允许在一秒钟内完成的最大请求数int burstCapacity = routeConfig.getBurstCapacity();try {// 限流key的名称(request_rate_limiter.{localhost}.timestamp,request_rate_limiter.{localhost}.tokens)List<String> keys = getKeys(id);// The arguments to the LUA script. time() returns unixtime in seconds.List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "",Instant.now().getEpochSecond() + "", "1");// allowed, tokens_left = redis.eval(SCRIPT, keys, args)// 执行LUA脚本Flux<List<Long>> flux = this.redisTemplate.execute(this.script, keys, scriptArgs);// .log("redisratelimiter", Level.FINER);return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))).reduce(new ArrayList<Long>(), (longs, l) -> {longs.addAll(l);return longs;}) .map(results -> {boolean allowed = results.get(0) == 1L;Long tokensLeft = results.get(1);Response response = new Response(allowed, getHeaders(routeConfig, tokensLeft));if (log.isDebugEnabled()) {log.debug("response: " + response);}return response;});}catch (Exception e) {log.error("Error determining if user allowed from redis", e);}return Mono.just(new Response(true, getHeaders(routeConfig, -1L)));
}
  1. 测试Redis限流器

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@RunWith(SpringRunner.class)
public class GatewayRateLimiterTest {private static final Logger LOGGER = LoggerFactory.getLogger(GatewayRateLimiterTest.class);@Rulepublic TestRule benchmarkRun = new BenchmarkRule();@ClassRulepublic static MockServerContainer mockServer = new MockServerContainer();@ClassRulepublic static GenericContainer redis = new GenericContainer("redis:5.0.6").withExposedPorts(6379);@AutowiredTestRestTemplate template;@BeforeClasspublic static void init() {System.setProperty("spring.cloud.gateway.routes[0].id", "account-service");System.setProperty("spring.cloud.gateway.routes[0].uri", "http://localhost:" + mockServer.getServerPort());System.setProperty("spring.cloud.gateway.routes[0].predicates[0]", "Path=/account/**");System.setProperty("spring.cloud.gateway.routes[0].filters[0]", "RewritePath=/account/(?<path>.*), /$\\{path}");System.setProperty("spring.cloud.gateway.routes[0].filters[1].name", "RequestRateLimiter");System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.redis-rate-limiter.replenishRate", "10");System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.redis-rate-limiter.burstCapacity", "20");System.setProperty("spring.redis.host", "localhost");System.setProperty("spring.redis.port", "" + redis.getMappedPort(6379));new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()).when(HttpRequest.request().withPath("/1")).respond(response().withBody("{\"id\":1,\"number\":\"1234567890\"}").withHeader("Content-Type", "application/json"));}@Test@BenchmarkOptions(warmupRounds = 0, concurrency = 6, benchmarkRounds = 600)public void testAccountService() {ResponseEntity<Account> r = template.exchange("/account/{id}", HttpMethod.GET, null, Account.class, 1);LOGGER.info("Received: status->{}, payload->{}, remaining->{}", r.getStatusCodeValue(), r.getBody(), r.getHeaders().get("X-RateLimit-Remaining"));
//      Assert.assertEquals(200, r.getStatusCodeValue());
//      Assert.assertNotNull(r.getBody());
//      Assert.assertEquals(Integer.valueOf(1), r.getBody().getId());
//      Assert.assertEquals("1234567890", r.getBody().getNumber());}}

执行Test类:
发现超过20之后会被拦截返回429,运行过程中随着令牌的放入会不断有请求成功。

14:20:32.242 --- [pool-2-thread-1] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[18]
14:20:32.242 --- [pool-2-thread-4] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[16]
14:20:32.242 --- [pool-2-thread-2] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[14]
14:20:32.242 --- [pool-2-thread-3] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[15]
14:20:32.242 --- [pool-2-thread-6] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[17]
14:20:32.242 --- [pool-2-thread-5] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[19]
14:20:32.294 --- [pool-2-thread-4] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[15]
14:20:32.297 --- [pool-2-thread-6] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[19]
14:20:32.304 --- [pool-2-thread-3] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[18]
14:20:32.308 --- [pool-2-thread-5] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[16]
14:20:32.309 --- [pool-2-thread-1] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[17]
14:20:32.312 --- [pool-2-thread-2] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[14]
14:20:32.320 --- [pool-2-thread-4] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[13]
14:20:32.326 --- [pool-2-thread-6] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[12]
14:20:32.356 --- [pool-2-thread-4] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[7]
14:20:32.356 --- [pool-2-thread-5] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[10]
14:20:32.361 --- [pool-2-thread-6] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[6]
14:20:32.363 --- [pool-2-thread-2] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[8]
14:20:32.384 --- [pool-2-thread-5] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[4]
14:20:32.384 --- [pool-2-thread-3] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[11]
14:20:32.386 --- [pool-2-thread-4] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[5]
14:20:32.390 --- [pool-2-thread-1] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[9]
14:20:32.391 --- [pool-2-thread-6] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[3]
14:20:32.392 --- [pool-2-thread-2] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[2]
14:20:32.403 --- [pool-2-thread-6] : Received: status->429, payload->null, remaining->[0]
14:20:32.403 --- [pool-2-thread-4] : Received: status->429, payload->null, remaining->[0]
........
14:20:33.029 --- [pool-2-thread-2] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[9]
14:20:33.033 --- [pool-2-thread-1] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[8]
14:20:33.033 --- [pool-2-thread-4] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[7]
14:20:33.037 --- [pool-2-thread-3] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[6]
14:20:33.039 --- [pool-2-thread-5] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[5]
14:20:33.046 --- [pool-2-thread-6] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[4]
14:20:33.052 --- [pool-2-thread-5] : Received: status->429, payload->null, remaining->[0]
14:20:33.058 --- [pool-2-thread-6] : Received: status->429, payload->null, remaining->[0]
14:20:33.058 --- [pool-2-thread-1] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[2]
14:20:33.060 --- [pool-2-thread-5] : Received: status->429, payload->null, remaining->[0]
14:20:33.081 --- [pool-2-thread-4] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[1]
14:20:33.082 --- [pool-2-thread-3] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[0]
14:20:33.084 --- [pool-2-thread-2] : Received: status->200, payload->Account(id=1, number=1234567890), remaining->[3]
14:20:33.088 --- [pool-2-thread-5] : Received: status->429, payload->null, remaining->[0]

如果默认的限流器不能够满足使用,可以通过继承AbstractRateLimiter实现自定义限流器,然后通过RouteLocator方式去注入拦截器。

Resilience4J熔断器

  1. 引入依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
  1. Resilience4J 断路器介绍

  • 三个一般性状态

    • CLOSED:关闭状态,放过所有请求,记录请求状态。
    • OPEN:打开,异常请求达到阀值数量时,开启熔断,拒绝所有请求。
    • HALF_OPEN:半开,放开一定数量的请求,重新计算错误率。
  • 两个特定状态

    • DISABLED:禁用
    • FORCED_OPEN:强开
  • 状态之间转换

    启动时断路器为CLOSE状态,在达到一定请求量至后计算请求失败率,达到或高于指定失败率后,断路进入open状态,阻拦所有请求,开启一段时间(自定义)时间后,断路器变为halfOpen状态,重新计算请求失败率。halfOpen错误率低于指定失败率后,断路进入close状态,否则进入open状态。

  1. 通过Resilience4J启用Spring Cloud Gateway断路器

要启用构建在Resilience4J之上的断路器,我们需要声明一个Customizer传递了的bean ReactiveResilience4JCircuitBreakerFactory。可以非常简单地去配置设置,下面使用默认配置进行测试

@Bean
public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id).circuitBreakerConfig(CircuitBreakerConfig.custom()//统计失败率的请求总数.slidingWindowSize(5) //在半开状态下请求的次数.permittedNumberOfCallsInHalfOpenState(5)//断路器打开的成功率.failureRateThreshold(50.0F)//断路器打开的周期.waitDurationInOpenState(Duration.ofMillis(30))//属于慢请求的周期.slowCallDurationThreshold(Duration.ofMillis(200))//慢请求打开断路器的成功率.slowCallRateThreshold(50.0F).build()).timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(200)).build()).build());
}
  1. 测试Resilience4J断路器

使用默认配置进行测试

@Beanpublic Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id).circuitBreakerConfig(CircuitBreakerConfig.ofDefaults()).circuitBreakerConfig(CircuitBreakerConfig.custom().slowCallDurationThreshold(Duration.ofMillis(200)).build()).timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(200)).build()).build());}

执行下面Test用例

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@RunWith(SpringRunner.class)
public class GatewayCircuitBreakerTest {private static final Logger LOGGER = LoggerFactory.getLogger(GatewayRateLimiterTest.class);@Rulepublic TestRule benchmarkRun = new BenchmarkRule();@ClassRulepublic static MockServerContainer mockServer = new MockServerContainer();@AutowiredTestRestTemplate template;final Random random = new Random();int i = 0;@BeforeClasspublic static void init() {System.setProperty("logging.level.org.springframework.cloud.gateway.filter.factory", "TRACE");System.setProperty("spring.cloud.gateway.routes[0].id", "account-service");System.setProperty("spring.cloud.gateway.routes[0].uri", "http://localhost:" + mockServer.getServerPort());System.setProperty("spring.cloud.gateway.routes[0].predicates[0]", "Path=/account/**");System.setProperty("spring.cloud.gateway.routes[0].filters[0]", "RewritePath=/account/(?<path>.*), /$\\{path}");System.setProperty("spring.cloud.gateway.routes[0].filters[1].name", "CircuitBreaker");System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.name", "exampleSlowCircuitBreaker");
//        System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.slowCallDurationThreshold", "100");
//        System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.slowCallRateThreshold", "9.0F");
//        System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.fallbackUri", "forward:/fallback/account");MockServerClient client = new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort());client.when(HttpRequest.request().withPath("/1")).respond(response().withBody("{\"id\":1,\"number\":\"1234567890\"}").withHeader("Content-Type", "application/json"));
//        client.when(HttpRequest.request()
//                .withPath("/2"), Times.exactly(3))
//              .respond(response()
//                        .withBody("{\"id\":2,\"number\":\"1\"}")
//                        .withDelay(TimeUnit.SECONDS, 1000)
//                        .withHeader("Content-Type", "application/json"));client.when(HttpRequest.request().withPath("/2")).respond(response().withBody("{\"id\":2,\"number\":\"1234567891\"}").withDelay(TimeUnit.SECONDS, 200).withHeader("Content-Type", "application/json"));}@Test@BenchmarkOptions(warmupRounds = 0, concurrency = 1, benchmarkRounds = 600)public void testAccountService() {int gen = 1 + (i++ % 2);ResponseEntity<Account> r = template.exchange("/account/{id}", HttpMethod.GET, null, Account.class, gen);LOGGER.info("{}. Received: status->{}, payload->{}, call->{}", i, r.getStatusCodeValue(), r.getBody(), gen);}}

请求日志如下:当请求达到100次时候,此时失败率为50% 这时候系统开启断路器返回503!

20:07:29.281 --- [pool-2-thread-1] : 91. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:07:30.297 --- [pool-2-thread-1] : 92. Received: status->504, payload->Account(id=null, number=null), call->2
20:07:30.316 --- [pool-2-thread-1] : 93. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:07:31.328 --- [pool-2-thread-1] : 94. Received: status->504, payload->Account(id=null, number=null), call->2
20:07:31.345 --- [pool-2-thread-1] : 95. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:07:32.359 --- [pool-2-thread-1] : 96. Received: status->504, payload->Account(id=null, number=null), call->2
20:07:32.385 --- [pool-2-thread-1] : 97. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:07:33.400 --- [pool-2-thread-1] : 98. Received: status->504, payload->Account(id=null, number=null), call->2
20:07:33.414 --- [pool-2-thread-1] : 99. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:07:34.509 --- [pool-2-thread-1] : 100. Received: status->504, payload->Account(id=null, number=null), call->2
20:07:34.525 --- [pool-2-thread-1] : 101. Received: status->503, payload->Account(id=null, number=null), call->1
20:07:34.533 --- [pool-2-thread-1] : 102. Received: status->503, payload->Account(id=null, number=null), call->2
20:07:34.539 --- [pool-2-thread-1] : 103. Received: status->503, payload->Account(id=null, number=null), call->1
20:07:34.545 --- [pool-2-thread-1] : 104. Received: status->503, payload->Account(id=null, number=null), call->2
20:07:34.552 --- [pool-2-thread-1] : 105. Received: status->503, payload->Account(id=null, number=null), call->1
20:07:34.566 --- [pool-2-thread-1] : 106. Received: status->503, payload->Account(id=null, number=null), call->2
20:07:34.572 --- [pool-2-thread-1] : 107. Received: status->503, payload->Account(id=null, number=null), call->1
20:07:34.576 --- [pool-2-thread-1] : 108. Received: status->503, payload->Account(id=null, number=null), call->2
20:07:34.580 --- [pool-2-thread-1] : 109. Received: status->503, payload->Account(id=null, number=null), call->1
20:07:34.586 --- [pool-2-thread-1] : 110. Received: status->503, payload->Account(id=null, number=null), call->2
20:07:34.591 --- [pool-2-thread-1] : 111. Received: status->503, payload->Account(id=null, number=null), call->1

这时候我们修改下配置

     @BeforeClasspublic static void init() {System.setProperty("logging.level.org.springframework.cloud.gateway.filter.factory", "TRACE");System.setProperty("spring.cloud.gateway.routes[0].id", "account-service");System.setProperty("spring.cloud.gateway.routes[0].uri", "http://localhost:" + mockServer.getServerPort());System.setProperty("spring.cloud.gateway.routes[0].predicates[0]", "Path=/account/**");System.setProperty("spring.cloud.gateway.routes[0].filters[0]", "RewritePath=/account/(?<path>.*), /$\\{path}");System.setProperty("spring.cloud.gateway.routes[0].filters[1].name", "CircuitBreaker");System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.name", "exampleSlowCircuitBreaker");
//        System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.slowCallDurationThreshold", "100");
//        System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.slowCallRateThreshold", "9.0F");System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.fallbackUri", "forward:/fallback/account");MockServerClient client = new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort());client.when(HttpRequest.request().withPath("/1")).respond(response().withBody("{\"id\":1,\"number\":\"1234567890\"}").withHeader("Content-Type", "application/json"));client.when(HttpRequest.request().withPath("/2"), Times.exactly(3)).respond(response().withBody("{\"id\":2,\"number\":\"1\"}").withDelay(TimeUnit.SECONDS, 1000).withHeader("Content-Type", "application/json"));client.when(HttpRequest.request().withPath("/2")).respond(response().withBody("{\"id\":2,\"number\":\"1234567891\"}")
//                        .withDelay(TimeUnit.SECONDS, 200).withHeader("Content-Type", "application/json"));}

新建一个回调接口,用于断路器打开后请求的地址。

@RestController
@RequestMapping("/fallback")
public class GatewayFallback {@GetMapping("/account")public Account getAccount() {Account a = new Account();a.setId(2);a.setNumber("123456");return a;}}

使用默认设置时,前3次请求触发断路器回调,后面正常请求成功

20:20:23.529 --- [pool-2-thread-1] : 1. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:20:23.777 --- [pool-2-thread-1] : 2. Received: status->200, payload->Account(id=2, number=123456), call->2
20:20:23.808 --- [pool-2-thread-1] : 3. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:20:24.018 --- [pool-2-thread-1] : 4. Received: status->200, payload->Account(id=2, number=123456), call->2
20:20:24.052 --- [pool-2-thread-1] : 5. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:20:24.268 --- [pool-2-thread-1] : 6. Received: status->200, payload->Account(id=2, number=123456), call->2
20:20:24.301 --- [pool-2-thread-1] : 7. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:20:24.317 --- [pool-2-thread-1] : 8. Received: status->200, payload->Account(id=2, number=1234567891), call->2
20:20:24.346 --- [pool-2-thread-1] : 9. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:20:24.363 --- [pool-2-thread-1] : 10. Received: status->200, payload->Account(id=2, number=1234567891), call->2
20:20:24.378 --- [pool-2-thread-1] : 11. Received: status->200, payload->Account(id=1, number=1234567890), call->1
20:20:24.392 --- [pool-2-thread-1] : 12. Received: status->200, payload->Account(id=2, number=1234567891), call->2
20:20:24.402 --- [pool-2-thread-1] : 13. Received: status->200, payload->Account(id=1, number=1234567890), call->1

END

至此给大家介绍了Spring Cloud Gateway中断路器跟限流器使用。

欢迎关注公众号!
公众号回复:入群 ,扫码加入我们交流群!

Spring Cloud

springcloud中Gateway的限流熔断机制!相关推荐

  1. Spirng Cloud 中gateway 网关限流和熔断

    分流:原先数据库只放一个服务器,无论多少个都只能访问这个服务器,访问不了就排队(延迟)(如果同一时间也高并发了那就限流) 限流:同一时间限制访问的人数 限流的算法 漏桶算法:把请求放到一个容器中,控制 ...

  2. Gateway的限流重试机制

    前言 想要源码地址的可以加上此微信:Lemon877164954 前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其他 ...

  3. 进一步学习spring cloud gateway的 限流熔断

    目前,Spring Cloud Gateway是仅次于Spring Cloud Netflix的第二个最受欢迎的Spring Cloud项目(就GitHub上的星级而言).它是作为Spring Clo ...

  4. Nacos,Sentine限流熔断,Gateway网关

    文章目录 建立项目 注册中心简介 背景分析 Nacos概述 构建Nacos服务 准备工作 If use MySQL as datasource: Count of DB: Connect URL of ...

  5. SpringCloud Alibaba微服务实战(五) - Sentinel实现限流熔断

    什么是Sentinel? 请查看文章:SpringCloud Alibaba微服务实战(一) - 基础环境搭建 构建服务消费者cloud-sentinel进行服务调用 服务创建请查看文章:Spring ...

  6. spring cloud gateway 之限流篇 1

    转载请标明出处: http://blog.csdn.net/forezp/article/details/85081162 本文出自方志朋的博客 个人博客纯净版:https://www.fangzhi ...

  7. 深度好文 — 微服务和API网关限流熔断实现关键逻辑思路

    来源:https://www.toutiao.com/i6853970319745483275/?group_id=6853970319745483275 今天准备谈下微服务架构和API网关中的限流熔 ...

  8. 实战 Spring Cloud Gateway 之限流篇

    来源:https://www.aneasystone.com/archives/2020/08/spring-cloud-gateway-current-limiting.html 话说在 Sprin ...

  9. sentinel 限流熔断神器详细介绍

    一.限流熔断神器 sentinel 1.什么是 sentinel: 在基于 SpringCloud 构建的微服务体系中,服务间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素 ...

最新文章

  1. linux 文件管理 教程,Linux文件管理
  2. python生成静态html_Python写静态HTML
  3. Android Studio打开DDMS : An error has occurred URIUtil
  4. resharper警告 :linq replace with single call to FirstOrDefault
  5. 贪心——今年暑假不AC(hdu2037)
  6. 【MyBatis框架】配置文件-resultMap总结
  7. 自定义控件(视图)2期笔记03:自定义控件之使用系统控件(优酷案例之广告条Viewpager)...
  8. easyui datagrid 不分页_快递物流管理系统(SSM,JQUERY-EASYUI,MYSQL)
  9. axis2 java demo_axis2 webservice实现
  10. android 7 audio架构,7.2.1 Audio系统的各个层次
  11. ADVHAT: REAL-WORLD ADVERSARIAL ATTACK ON ARCFACE FACE ID SYSTEM 笔记
  12. Ubuntulinux离线安装ClamTk杀毒软件步骤和使用方法
  13. 中国女篮姐妹花杨舒予、杨力维成为护肤品牌佰草集太极首组代言人
  14. 带sex的net域名_域名劫持的几种方法、域名劫持有什么方式
  15. 解决Cipher Suites导致的“未能创建 SSL/TLS 安全通道”异常问题
  16. 互联网-2互联网思维特点和理念
  17. python异常数据筛选_学习笔记(06):Python数据清理实践-数据过滤,06Python,清洗,实战,筛选...
  18. Qt为exe添加ico图片
  19. ASEMI整流桥GBP406~GBP410,GBP406参数,GBP410图片
  20. python音乐可视化壁纸_Python 制作一个漂亮的音乐节奏可视化效果

热门文章

  1. root android 10 apk,安卓手机必备,免root提取分享APK
  2. 电脑课脱离老师控制(极域电子教室)win7 小白专用
  3. oracle的课题都有哪些内容,一份完整的课题包括哪些内容
  4. 金蝶K3 V12.0 常用数据表(整理)
  5. 感应电动机和异步电动机的区别
  6. 后市策略是什么?沪指再创新高怎么办? 可高抛!
  7. 一张表格分成两页打印_excel一个表格自动分成两页怎么办
  8. Python头像动漫化,快来生成一个自己的动漫头像吧,带你一步一步深入Handler源码
  9. 免费可用的STUN server
  10. 计算机网络tcp三次握手分析,tcp三次握手及原理