使用Spring Boot + Resilience 4j实现断路器

Resilience 4j提供以下功能。

  • 断路器
  • RateLimiter
  • 舱壁
  • 重试
  • 高速缓存
  • TimeLimiter
  • 如果您打算在Spring Boot中使用它,可以使用Starter。请注意,Spring Boot 1.x和2.x系列之间的artifactId似乎有所不同。另外,上面只包含CircuitBreaker和RateLimiter,在使用其他功能时需要单独添加依赖项。(由于未准备好AutoConfigure,您还需要自己定义bean。)

    这次我将总结如何在Spring Boot 2.x系列中使用CircuitBreaker和RateLimiter。

    环境

    • JDK 8
    • Spring Boot 2.1.2.RELEASE
    • Resilience 4j 0.13.2

    断路器

    当某些具有微服务的服务发生故障时,可以临时阻止对故障服务的访问并防止故障传播。

    CircuitBreaker有三种状态:Closed,Open,HalfOpen。如果是正常的,则它是关闭的,如果处理失败超过一定数量,它将变为打开并且访问被阻止。当在打开状态下经过一段时间后,进入HalfOpen状态。如果处理在HalfOpen状态下失败超过一定量,则返回到关闭状态。

    在Resilience 4j中,处理的成功和失败由环形缓冲器Ring Bit Buffer管理,并且当缓冲器中的故障数超过设定的速率时,状态转变。

    断路器使用状态中的Ring Bit Buffer CLOSED来存储呼叫的成功或失败状态。成功的呼叫存储为0位,失败的呼叫存储为1位。Ring Bit Buffer具有(可配置的)固定大小。环位缓冲区在内部使用类似数据结构的BitSet来存储与布尔数组相比节省内存的位。BitSet使用long []数组来存储这些位。这意味着BitSet只需要一个包含16个长(64位)值的数组来存储1024个调用的状态。

    例如,如果环形缓冲区的大小为10,则必须至少评估10个调用,然后才能计算故障率。如果仅评估了9个呼叫,即使所有9个呼叫都失败,断路器也不会打开。

    用于Closed - > Open和HalfOpen - > Closed判断的环形缓冲区是不同的,可以定义大小,但使用相同的判断条件(错误率)。

    在持续关闭时间结束后,断路器状态从OPEN更改为HALF_OPEN并允许调用以查看后端是否仍然不可用或已再次可用。

    断路器使用另一个(可配置的)环位缓冲区来评估HALF_OPEN状态中的故障率。如果故障率高于配置的阈值,则状态将更改回OPEN。如果故障率低于或等于阈值,则状态变回CLOSED。

    此外,处理的成功和失败由异常判断。默认情况下,如果任何异常抛出异常,则会将其视为处理失败,但您也可以指定要将其视为失败的条件。

    设置

    application.yml你可以设置定义多个断路器。

    resilience4j:
        circuitbreaker:
            backends:
                circuitA: # #断路器名
                    truering-buffer-size-in-closed-state: 5 #环形缓冲区是在封闭状态下使用的大小
                    ring-buffer-size-in-half-open-state: 3 # HalfOpen 状态下的大小
                    wait-duration-in-open-state : 5000 # Open持续时间
                    failure-rate-threshold: 50 # 到打开状态的阈值
                    record-failure-predicate: com.example.resilience.RecordFailurePredicate
                    ignore-exceptions: #没有失败#异常类和计数
                        - com.example.resilience.exception.BusinessException
                    record-exceptions: #异常类失败和计数
                        - com.example.resilience.exception.SystemException
                circuitB:
                    ・・・
    

    如果你想只考虑一个特定的异常和故障使用RecordExceptions,当你不想忽视特定的异常时使用ignoreExceptions。

    有两种方法可以使用Spring AOP并在函数中实现它。无论哪种实现,如果Circuit处于Open状态,它将生成CircuitBreakerOpenException。

    在以下实现示例中,为简单起见,它不是微服务。最初RestTemplate,我认为这将是Service Class 调用其他服务API等使用等的过程。

    Spring AOP实现

    通过@CircuitBreaker(name = "hogehoge")注释到类或方法上则可以启用断路器。如果在类指定这个注释,则为所有公共方法启用断路器。

    @Service
    @CircuitBreaker(name = "circuitB")
    public class CircuitBreakerService {
        public String aop(String str) {
            if (str == null) {
                throw new RuntimeException();
            }
            return "success!!";
        }
    }
    

    调用者不用考虑任何事情,只需执行该方法即可。

    @RestController
    @RequestMapping("/circuit")
    public class CircuitBreakerController {
        private final CircuitBreakerService service;
    

        public CircuitBreakerController(CircuitBreakerService service) {
            this.service = service;
        }

        @GetMapping("/aop")
        public String aop(@RequestParam(required = false) String str) {
            return service.aop(str);
        }
    }

    如何写业务函数?

    @Service
    public class CircuitBreakerService {
        public String func(String str) {
            if (str == null) {
                throw new RuntimeException();
            }
            return "success!!";
        }
    }
    

    调用端使用断路器的decorate~方法修饰要调用的方法。

    @RestController
    @RequestMapping("/circuit")
    public class CircuitBreakerController {
        private final CircuitBreaker circuitBreaker;
        private final CircuitBreakerService service;
    

        public CircuitBreakerController(CircuitBreakerRegistry registry, CircuitBreakerService service) {
            this.circuitBreaker = registry.circuitBreaker(“circuitA”);
            this.service = service;
        }

        @GetMapping("/func")
        public String func(@RequestParam(required = false) String str) {
            return CircuitBreaker.decorateSupplier(circuitBreaker, () -> service.func(str)).get();
        }
    }

    后备处理

    接下来,如果发生故障,执行回退过程怎么办?在Hystrix 的情况下,通过指定@HystrixCommand("hogeMethod"),由于Resilience4j没有设置的这样的功能,必须自己实现。

    @RestController
    @RequestMapping("/circuit")
    public class CircuitBreakerController {
        private final CircuitBreaker circuitBreaker;
        private final CircuitBreakerService service;
    

        public CircuitBreakerController(CircuitBreakerRegistry registry, CircuitBreakerService service) {
            this.circuitBreaker = registry.circuitBreaker(“circuitA”);
            this.service = service;
        }

        @GetMapping("/func")
        public String func(@RequestParam(required = false) String str) {
            return Try.ofSupplier(CircuitBreaker.decorateSupplier(circuitBreaker, () -> service.func(str)))
                    .recover(CircuitBreakerOpenException.class,
    “Circuit is Open!!”)
                    .recover(RuntimeException.class,
    “fallback!!”).get();
        }
    }

    完整的源代码位于下方。https://github.com/d-yosh/spring-boot-resilience4j-example

    RateLimiter

    您可以限制每单位时间的执行次数。

    单位时间是一个周期,并且可以在一个周期中执行的数量是有限的。如果它超过了可以在一个循环中执行的上限,则让它等待,如果等待时间超过超时时间,则发生RequestNotPermitted。

    在application.yml可以定义多个RateLimiter。

    resilience4j:

        ratelimiter:

            limiters:

                limiterA: # #Rate​​Limiter名称

                    limit-for-period: 1 # 每时间单位#可执行处理数

                    limit-refresh-period-in-millis: 10000 # #单位时间(毫秒)

                    timeout-in-millis: 10000 #timeout time(milliseconds)

                limiterB:

                ・・・

    它与断路器实现方式相同,有两种方法可以使用Spring AOP并在业务函数中编写它。实现方法也类似于断路器。

    @Service
    @RateLimiter(name = "limiterB")
    public class RateLimiterService {
        public String func() {
            return LocalDateTime.now().toString();
        }
    }
    

    调用者不用考虑任何事情,只需执行该方法即可。

    @RestController
    @RequestMapping("/ratelimiter")
    public class RateLimiterController {
        private final RateLimiterService service;
    

        public RateLimiterController(RateLimiterService service) {
            this.service = service;
        }

        @GetMapping(“aop”)
        public String aop() {
            return service.aop();
        }
    }

    函数方法:

    @Service
    public class RateLimiterService {
        public String func() {
            return LocalDateTime.now().toString();
        }
    }
    

    @RestController
    @RequestMapping("/ratelimiter")
    public class RateLimiterController {
        private final RateLimiter rateLimiter;
        private final RateLimiterService service;

        public RateLimiterController(RateLimiterRegistry registry, RateLimiterService service) {
            this.rateLimiter = registry.rateLimiter(“limiterA”);
            this.service = service;
        }

        @GetMapping(“func”)
        public String func() {
            return Try.ofSupplier(RateLimiter.decorateSupplier(rateLimiter, service::func))
                    .recover(RequestNotPermitted.class,
    “Request Not Permitted!!”).get();
        }
    }

    后备处理,与断路器一样,没有自动执行回退处理的机制,因此您需要自己实现它。

    完整的源代码位于下方。https://github.com/d-yosh/spring-boot-resilience4j-example

    单位时间为5秒,超时时间为1秒,每单位时间的执行次数为1。如果同时发送多个请求,则会发出失败请求。(如果您同时请求三个,则至少一个将始终失败。)

    $ curl http://localhost:8080/ratelimiter/func
    2019-01-22T23:09:35.612
    $ curl http://localhost:8080/ratelimiter/func
    Request Not Permitted!!
    

     

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值