深入学习spring cloud gateway 限流熔断( 四 )


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状态 。

深入学习spring cloud gateway 限流熔断

文章插图
 
状态转换
  1. 通过Resilience4J启用Spring Cloud Gateway断路器
要启用构建在Resilience4J之上的断路器,我们需要声明一个Customizer传递了的bean
ReactiveResilience4JCircuitBreakerFactory 。可以非常简单地去配置设置,下面使用默认配置进行测试
@Beanpublic 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);}}


推荐阅读