目前,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 gateway 限流熔断相关推荐

  1. Spring Cloud Gateway 限流适配多规则的解决方案

    Spring Cloud Gateway 限流适配多规则的解决方案 大家也可以关注我的公众号:浆果捕鼠草,文章也会同步更新,当然,公众号还会有一些资源可以分享给大家~ 首先要说明,本文是使用的 Spr ...

  2. 这可能是全网Spring Cloud Gateway限流最完整的方案了!

        作者:aneasystone     https://www.aneasystone.com/ 话说在 Spring Cloud Gateway 问世之前,Spring Cloud 的微服务世 ...

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

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

  4. Spring cloud 多种限流方案

    在频繁的网络请求时,服务有时候也会受到很大的压力,尤其是那种网络攻击,非法的.这样的情形有时候需要作一些限制.例如:限制对方的请求,这种限制可以有几个依据:请求IP.用户唯一标识.请求的接口地址等等. ...

  5. spring cloud gateway 之限流篇

    转载请标明出处: https://www.fangzhipeng.com 本文出自方志朋的博客 在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方 ...

  6. spring cloud gateway 之限流篇 1

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

  7. 微服务网关spring cloud gateway入门详解

    1.API网关 API 网关是一个处于应用程序或服务( REST API 接口服务)之前的系统,用来管理授权.访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的 ...

  8. 硬核!从0到1学习Spring Cloud微服务章节《建议收藏》

    为什么要使用学习springcloud以及他的优势? Spring Cloud 来源于 Spring,质量.稳定性.持续性都可以得到保证. spirng Cloud 天然支持 Spring Boot, ...

  9. 熬夜肝了这篇Spring Cloud Gateway的功能及综合使用

    前言 SpringCloud 是微服务中的翘楚,最佳的落地方案. Spring Cloud Gateway 是 Spring Cloud 新推出的网关框架,之前是 Netflix Zuul.网关通常在 ...

最新文章

  1. java rsa 存数据库_java – 如何创建RSA密钥对以便将其存储在数据库中?
  2. Android 动态设置布局文件的exception
  3. vue 强制刷新组件
  4. Android 系统(73)---Android判断你的应用在前台还是在后台
  5. mysql中连接和断开数据库_robot framework——连接和断开mysql数据库
  6. 用户界面和兼容性测试
  7. android通话流程浅析RIL层
  8. Python rsa 加密
  9. TextView的一些高级应用(自定义字体、显示多种颜色、添加阴影)
  10. python学习系列--str类型
  11. sed学习[参考转载]
  12. 如何让你的网站每天自动更新
  13. Android RecyclerView使用ItemDecoration刻画时间线/时间轴/时光轴timeline
  14. 基于微信小程序的毕业设计题目(35)PHP医院预约挂号小程序(含开题报告、任务书、中期报告、答辩PPT、论文模板)
  15. java读取图片文字
  16. 禅道管理员admin密码登录失败,更改密码
  17. caffe 人脸关键点检测_密集人脸关键点检测
  18. java 统计阅读量_使用redis实现【统计文章阅读量】及【最热文章】功能
  19. 计算机中怎么设置纸张大小,针式打印机设置,教您针式打印机怎么设置纸张大小...
  20. 多柱汉罗塔(python解法,带注释,注释为个人理解)

热门文章

  1. 佳佳GIS学习笔记2
  2. 阿里云时空数据库实战(一):数据入库与导出
  3. SparkSubmit提交任务到yarn及报错解决方案
  4. mysql unknown_mysql碰到unknown variable 'xxxx' 的解决方法
  5. 制作自己的openwrt刷机包_手机刷机包如何制作?自己如何制作刷机包?
  6. textView 的设置文本中某一文字的字体颜色以及图文混排
  7. iOS代码混淆原理初探
  8. 关于visual studio下载过慢的问题
  9. java itext根据模板生成pdf
  10. 秒懂1U、2U、4U和42U服务器