SpringCloud集成Resilience4j实现熔断器

前言

在文章《小谈Springcloud中的几个主流熔断器》我们介绍了SpingCloud架构中的几个主流熔断器,其中SpringCloud官方推荐的Resilience4j作为2020.x以后的新秀,远远没有hystrix有名,相关的文档也还不够多;今天这个文章就来讲讲SpringCloud如何使用Resilience4j实现熔断器;

SpringCloud集成Resilience4j实现熔断器_第1张图片

Resilience4j

resilience4j是一个轻量级、易于使用的容错库,其灵感来自Netflixesilience4j是一个轻量级、易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计。

Resilience4j提供高阶函数(decorators)来增强任何功能接口、lambda表达式或方法引用,包括断路器、速率限制器、重试或舱壁。可以在任何函数接口、lambda表达式或方法引用上使用多个装饰器。

Resilience4j非常轻量级,不仅可以在SpringCloud进行使用,还可以直接在自己的Java程序里通过Resilience4j的API实现Rate Limite的功能; 这点也是笔者非常认同的一点; 支持和扩展上更为方便; 今天的这个文章,重点还是介绍如何在SpringCloud里使用Resilicen4j

导入依赖包

通过pom.xml引入对Resilience4j的依赖


    io.github.resilience4j
    resilience4j-spring-boot

配置resilience4j

在项目的applicaiton.yml里添加SpringCloud里resilience4j的相关配置

resilience4j.circuitbreaker:
  configs:
    default:
      registerHealthIndicator: true
      slidingWindowSize: 10
      minimumNumberOfCalls: 5
      permittedNumberOfCallsInHalfOpenState: 3
      automaticTransitionFromOpenToHalfOpenEnabled: true
      waitDurationInOpenState: 5s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
      recordExceptions:
        - org.springframework.web.client.HttpServerErrorException
        - java.util.concurrent.TimeoutException
        - java.io.IOException
      ignoreExceptions:
        - com.kxblive.common.error.CustomException
    shared:
      slidingWindowSize: 100
      permittedNumberOfCallsInHalfOpenState: 30
      waitDurationInOpenState: 1s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
      ignoreExceptions:
        - com.kxblive.common.error.CustomException
  instances:
    backendA:
      baseConfig: default
    backendB:
      registerHealthIndicator: true
      slidingWindowSize: 10
      minimumNumberOfCalls: 10
      permittedNumberOfCallsInHalfOpenState: 3
      waitDurationInOpenState: 5s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
      recordFailurePredicate: com.kxblive.common.error.FailureExceptionPredicate
resilience4j.retry:
  configs:
    default:
      maxAttempts: 3
      waitDuration: 100
      retryExceptions:
        - org.springframework.web.client.HttpServerErrorException
        - java.util.concurrent.TimeoutException
        - java.io.IOException
      ignoreExceptions:
        - com.kxblive.common.error.CustomException
  instances:
    backendA:
      baseConfig: default
    backendB:
      baseConfig: default
resilience4j.bulkhead:
  configs:
    default:
      maxConcurrentCalls: 100
  instances:
    backendA:
      maxConcurrentCalls: 10
    backendB:
      maxWaitDuration: 10ms
      maxConcurrentCalls: 20

resilience4j.thread-pool-bulkhead:
  configs:
    default:
      maxThreadPoolSize: 4
      coreThreadPoolSize: 2
      queueCapacity: 2
  instances:
    backendA:
      baseConfig: default
    backendB:
      maxThreadPoolSize: 1
      coreThreadPoolSize: 1
      queueCapacity: 1

resilience4j.ratelimiter:
  configs:
    default:
      registerHealthIndicator: false
      limitForPeriod: 10
      limitRefreshPeriod: 1s
      timeoutDuration: 0
      eventConsumerBufferSize: 100
  instances:
    backendA:
      baseConfig: default
    backendB:
      limitForPeriod: 6
      limitRefreshPeriod: 500ms
      timeoutDuration: 3s

resilience4j.timelimiter:
  configs:
    default:
      cancelRunningFuture: false
      timeoutDuration: 2s
  instances:
    backendA:
      baseConfig: default
    backendB:
      baseConfig: default

在这个配置中分别配置了circuitbeaker、retry、bulkhead、ratelimiter, timelimiter,thread-pool-bulkhead这些相关的熔断限流的相关项目。引入resilience4j-spring-boot集成了circuitbeaker、retry、bulkhead、ratelimiter几个模块的功能, 提供相关的实现;我们可以根据业务的实际需要对这些相关项目进行具体配置修改;

开发上和使用Hystrix大致上一样; 不过注意调整一下的地方

feign契约只能用feign的, 不能用SpringMVC的契约

feign契约只能用feign的, 不能用SpringMVC的契约;所以不能使用默认的方式,必须通过autoconfiguraiton进行修改

    @Bean
    public Contract feignContract(){
        return new Contract.Default();
    }

    /**
    @Bean
    public Contract feignContract(){
        return new SpringMvcContract();
    }
    **/

使用标准Feign注解

由于修改了feign的constract方式;所以默认的feign注解方式,不能使用SpringMVC的方式,必须使用Feign的标准方式

// after Hystrix is removed from SpringCloud2021.0.1, the fallback is ineffective
//@FeignClient(name = "${codeman.service.name:codeman}", url = "${codeman.service.address:}", fallback = CodeManFallbackImpl.class)
public interface CodeManFeign extends CodeManService {

    @RequestLine("GET /codeman/info/version")
    public String getVersion();

    @RequestLine("GET /codeman/info/author")
    public String getAuthor();

    @RequestLine("GET /codeman/info/author/{userid}")    //对应请求方式和路径
    public String requestLine(@Param("userid") String userid);
}

通过上面的代码可以看到在Feign的定义接口里, 不再使用SpringMVC里的标准Post/Get/Delete/Request等; 而是使用Feign标准的注解RequestLine;这个估计大多数做过Feign的朋友,还不知道这个才是Feign的标准Annotation; 由于上一步,我们修改了Consract不再使用SpringMVCConstract;所以Post/Get/Delete/Request等这些在SpringMVC里的Annotation不能使用;必须替换;

业务调用

上一段代码,我们定义出了RPC的remote调用接口;在service层,我们使用已定义的Feign接口,完成业务上的调用;

@Component
public class CodeManServiceImpl implements CodeManService{
    //@Autowired
    //CodeManFeign codeManFeign;

    @Value("${codeman.service.address:http://${codeman.service.name:codeman}}")
    private String url;

    @Value("${codeman.service.name:codeman}")
    private String name;

    CircuitBreaker circuitBreaker;
    CodeManFeign codeManFeign;

    @PostConstruct
    public void init(){
        circuitBreaker = CircuitBreaker.ofDefaults("backendA");

        FeignDecorators decorators = FeignDecorators.builder()
                .withFallback(new CodeManFallbackImpl(), FeignException.class)
                .build();

        codeManFeign = Resilience4jFeign.builder(decorators).target(new Target.HardCodedTarget<>(CodeManFeign.class,
                name, url));
    }

    @Override
    public String getVersion() {
        return codeManFeign.getVersion();
    }

    @Override
    public String getAuthor() {
        return codeManFeign.getAuthor();
    }

    @Override
    public String requestLine(String userid) {
        return codeManFeign.requestLine(userid);
    }
}

上面的业务调用的代码;和我们经常使用OpenFeign进行调用上有点差别;在与对CodeManFeign的IOC的获取上;可以看到代码里

注释掉了通过IOC的方式

//@Autowired

//CodeManFeign codeManFeign;

而是通过

@PostConstruct

public void init(){

}

的方式,在init里通过FeignDecorators.builder()去实例化了Resilience4jFeign.builder(decorators).target()的方式去实例化了这个; 这里也是不解的地方;为什么不去类似Hystrix的哦方式。在Feign接口定义的时候,也去做一些绑定,而是要手工在这里通过API进行绑定; 对这个不解:个人的感觉;可能还是在产品设计的时候,让这个设置更加的细化吧。

在我们自己的项目里,在这里是自己做了处理的,其实这个处理的目的就省略掉Init里的代码

定义Fallback的实现

public class CodeManFallbackImpl implements CodeManService {
    @Override
    public String getVersion() {
        return "N/A";
    }

    @Override
    public String getAuthor() {
        return "SpringCloud";
    }

    @Override
    public String requestLine(String userid){
        return "Kill team‘s poison";
    }
}

通过以上的步骤,就可以实现Resilience4j的熔断器的实现了。 题外话,由于我们自己的项目都是使用的springcloud来实现微服务;并且都是在springcloud的基础上结合公司项目的特点,封装了自己的springcloud的开发框架,在springcloud的ioc是个好东西,但是在spring里的ioc到处都是,不仅增加了调试问题和追踪问题的复杂程度;而且更让人不放心的就是ioc都是使用反射或者动态代理的方式进行处理的, 这样的实现方式真的存在着很大的隐患;不过由于我们使用SpringCloud实现的微服务大多数都是来处理业务应用,反射的性能低下相对于业务应用往往性能的瓶颈而言,基本上都可以忽略不计了,这样的隐患也就藏起来了。 当然在学习了go语言以后,深深的体会到了,做服务和做业务对设计模式的取舍点的不同;

 

结束语

本文主要介绍的是在SpringCloud微服务开发中,如何使用Resilience4j实现熔断器功能的实现方式; 也可以直接通过resilience4j的API,在自己的Java程序里实现熔断器功能, 说实在的,SpringCloud的微服务体系真的和Service Mesh的体系一比较,真的就是一个小孩和大人的比较,熔断是业务需求吗,既然不是,为什么springCloude的开发中,微服务的代码却要揉入熔断器的开发代码; 别扭吧; 这就是Service Mesh为什么会是未来的原因。

SpringCloud集成Resilience4j实现熔断器_第2张图片

你可能感兴趣的:(springboot,微服务,spring,cloud,spring,cloud,java,spring,boot)