一、Resilience4j 实现断路器

创建SpringBoot 项目 resilience4j-circuitbreaker-demo

1.1 依赖

  1. <dependency>
  2. <groupId>io.github.resilience4j</groupId>
  3. <artifactId>resilience4j-spring-boot2</artifactId>
  4. <version>0.14.1</version>
  5. </dependency>

1.2 配置

  1. resilience4j:
  2. circuitbreaker: #断路器
  3. backends:
  4. menu: #menu 熔断器
  5. event-consumer-buffer-size: 10
  6. failure-rate-threshold: 50
  7. ring-buffer-size-in-closed-state: 5
  8. ring-buffer-size-in-half-open-state: 3
  9. wait-duration-in-open-state: 5000
  10. order: #order 熔断器
  11. event-consumer-buffer-size: 10
  12. failure-rate-threshold: 50
  13. ring-buffer-size-in-closed-state: 5
  14. ring-buffer-size-in-half-open-state: 3
  15. wait-duration-in-open-state: 5000

1.3 注册和处理熔断

  1. @RestController
  2. @RequestMapping("/customer")
  3. @Slf4j
  4. public class CustomerController {
  5. @Autowired
  6. private CoffeeService coffeeService;
  7. @Autowired
  8. private CoffeeOrderService coffeeOrderService;
  9. private CircuitBreaker circuitBreaker;
  10. /**
  11. * 代码方式
  12. * 注册 "menu" 断路器
  13. * 注册 "menu" 限流器
  14. */
  15. public CustomerController(CircuitBreakerRegistry circuitBreakerRegistry) {
  16. circuitBreaker = circuitBreakerRegistry.circuitBreaker("menu");
  17. }
  18. @GetMapping("/getCoffeeMenu")
  19. public List<String> getCoffeeMenu() {
  20. return Try.ofSupplier(
  21. CircuitBreaker.decorateSupplier(circuitBreaker,
  22. () -> coffeeService.coffee()))
  23. .recover(CircuitBreakerOpenException.class, Collections.emptyList()) //熔断处理
  24. .get(); //返回结果
  25. }
  26. /**
  27. * @return
  28. * @io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker 注解方式断路器 order
  29. */
  30. @PostMapping("order")
  31. @io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker(name = "order")
  32. public Map<String, Object> createOrder() {
  33. Map<String, Object> stringObjectMap = new HashMap<>();
  34. stringObjectMap.put("name", "布卡");
  35. log.info("Order {}", stringObjectMap);
  36. return coffeeOrderService.coffeeOrder(stringObjectMap);
  37. }
  38. }

1.4 测试

  • docker 启动 consul 服务注册中心
  • 启动项目 consul-waiter-service
  • 启动项目 resilience4j-circuitbreaker-demo ```shell

    执行

    curl -X GET http://localhost:8011/customer/getCoffeeMenu

    返回

    [“38029 “,”ESPRESSO COFFEES $12”,”HANDMADE COFFEES $15”,”SOOTHING HOT ALTERNATIVES $20”,”COLD ALTERNATIVES $8”]

关闭consul-waiter-service项目

快速执行多次

curl -X GET http://localhost:8011/customer/getCoffeeMenu

返回[],熔断生效

  1. <a name="KGaAi"></a>
  2. ## 二、Resilience4j 实现限流
  3. <a name="bulkhead"></a>
  4. ### 2.1 bulkhead
  5. 复制项目resilience4j-circuitbreaker-demo 修改为 bulkhead-customer-service
  6. <a name="09cd0eaf"></a>
  7. #### 2.1.1 配置
  8. ```yaml
  9. resilience4j:
  10. circuitbreaker: #断路器
  11. backends:
  12. menu: #menu 熔断器
  13. event-consumer-buffer-size: 10
  14. failure-rate-threshold: 50
  15. ring-buffer-size-in-closed-state: 5
  16. ring-buffer-size-in-half-open-state: 3
  17. wait-duration-in-open-state: 5000
  18. order: #order 熔断器
  19. event-consumer-buffer-size: 10
  20. failure-rate-threshold: 50
  21. ring-buffer-size-in-closed-state: 5
  22. ring-buffer-size-in-half-open-state: 3
  23. wait-duration-in-open-state: 5000
  24. #限流控制
  25. bulkhead:
  26. backends:
  27. menu:
  28. max-concurrent-call: 5 #最多5个线程
  29. max-wait-time: 5 #最多等待5s
  30. order:
  31. max-concurrent-call: 1
  32. max-wait-time: 5

2.1.2 注册和限制

  1. @RestController
  2. @RequestMapping("/customer")
  3. @Slf4j
  4. public class CustomerController {
  5. @Autowired
  6. private CoffeeService coffeeService;
  7. @Autowired
  8. private CoffeeOrderService coffeeOrderService;
  9. private CircuitBreaker circuitBreaker;
  10. private Bulkhead bulkhead;
  11. /**
  12. * 代码方式
  13. * 注册 "menu" 断路器
  14. * 注册 "menu" 限流器
  15. */
  16. public CustomerController(CircuitBreakerRegistry circuitBreakerRegistry,
  17. BulkheadRegistry bulkheadRegistry) {
  18. circuitBreaker = circuitBreakerRegistry.circuitBreaker("menu");
  19. bulkhead = bulkheadRegistry.bulkhead("menu");
  20. }
  21. @GetMapping("/getCoffeeMenu")
  22. public List<String> getCoffeeMenu() {
  23. return Try.ofSupplier(
  24. Bulkhead.decorateSupplier(bulkhead,
  25. CircuitBreaker.decorateSupplier(circuitBreaker,
  26. () -> coffeeService.coffee())))
  27. .recover(CircuitBreakerOpenException.class, Collections.emptyList()) //熔断处理
  28. .recover(BulkheadFullException.class, Collections.emptyList())
  29. .get(); //返回结果
  30. }
  31. /**
  32. * @return
  33. * @io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker 注解方式断路器 order
  34. * @io.github.resilience4j.bulkhead.annotation.Bulkhead 注解方式限流 order
  35. */
  36. @PostMapping("order")
  37. @io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker(name = "order")
  38. @io.github.resilience4j.bulkhead.annotation.Bulkhead(name = "order")
  39. public Map<String, Object> createOrder() {
  40. Map<String, Object> stringObjectMap = new HashMap<>();
  41. stringObjectMap.put("name", "布卡");
  42. log.info("Order {}", stringObjectMap);
  43. return coffeeOrderService.coffeeOrder(stringObjectMap);
  44. }
  45. }

2.1.3 测试

  • docker 启动 consul 服务注册中心
  • 启动项目 consul-waiter-service
  • 启动项目 bulkhead-customer-service
  • 安装 ab 性能测试工具 ```shell sudo apt-get update sudo apt-get install apache2-utils

测试

-n 总的请求数 -c 一次同时并发的请求数 ab -n 10 -c 5 http://localhost:8011/customer/getCoffeeMenu

  1. - 输出结果
  2. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/438760/1596377597208-28351c26-bfdf-4091-b832-1cf6415639a4.png#align=left&display=inline&height=459&margin=%5Bobject%20Object%5D&name=image.png&originHeight=918&originWidth=1048&size=181003&status=done&style=none&width=524)
  3. - 加大并发(结果有13个失败)
  4. ```shell
  5. ab -n 20 -c 10 http://localhost:8011/customer/getCoffeeMenu

image.png

2.2 RateLimiter

创建项目 ratelimiter-waiter-service

2.2.1 配置

  1. resilience4j:
  2. ratelimiter:
  3. limiters:
  4. coffee:
  5. limit-for-period: 5 #限制5次
  6. limit-refresh-period-in-millis: 30000 #30秒
  7. register-health-indicator: true
  8. subscribe-for-events: true
  9. timeout-in-millis: 5000 #5秒超时
  10. order:
  11. limit-for-period: 3
  12. limit-refresh-period-in-millis: 30000
  13. register-health-indicator: true
  14. subscribe-for-events: true
  15. timeout-in-millis: 1000

2.2.2 注册RateLimiter

  1. @Slf4j
  2. @RestController
  3. public class WaiterController {
  4. private RateLimiter rateLimiter;
  5. public WaiterController(RateLimiterRegistry rateLimiterRegistry) {
  6. //非注解方式,注册限流器
  7. rateLimiter = rateLimiterRegistry.rateLimiter("coffee");
  8. }
  9. /**
  10. * @param request
  11. * @return
  12. */
  13. @GetMapping(value = "/coffee", produces = "application/json")
  14. public List<String> coffeeMenu(HttpServletRequest request) {
  15. List<String> result = null;
  16. try {
  17. result = rateLimiter.executeSupplier(() -> Arrays.asList(request.getServerPort() + " "
  18. , "ESPRESSO COFFEES $12"
  19. , "HANDMADE COFFEES $15"
  20. , "SOOTHING HOT ALTERNATIVES $20"
  21. , "COLD ALTERNATIVES $8")
  22. );
  23. log.info("Get Order: {}", result);
  24. } catch (RequestNotPermitted e) {
  25. log.warn("Request Not Permitted! {}", e.getMessage(), e);
  26. }
  27. return result;
  28. }
  29. /**
  30. * 注解方式限流
  31. *
  32. * @param params
  33. * @return
  34. */
  35. @io.github.resilience4j.ratelimiter.annotation.RateLimiter(name = "order")
  36. @PostMapping(value = "/order", consumes = "application/json")
  37. public Map<String, Object> coffeeMenu(@RequestBody Map<String, Object> params) {
  38. log.info("order coffee");
  39. params.put("status", "success");
  40. params.put("payMoney", "$19");
  41. return params;
  42. }
  43. }

2.2.3 测试

  • 本地docker 启动consul 服务注册中心
  • 启动项目 bulkhead-customer-service
  • 启动项目 ratelimiter-waiter-service

访问 http://localhost:8011/customer/getCoffeeMenu ,接口限制30秒内访问5次

项目地址

https://github.com/h-dj/SpringCloud-Learning

参考