SpringCloud integrates Resilience4j to implement fuses

foreword

In the article "Talking about several mainstream fuses in Springcloud", we introduced several mainstream fuses in the SpingCloud architecture. Among them, Resilience4j officially recommended by SpringCloud is a rookie after 2020.x, which is far less famous than hystrix. Related documents Not enough; today's article will talk about how SpringCloud uses Resilience4j to implement fuses;

Resilience4j

resilience4j is a lightweight, easy-to-use fault tolerance library inspired by Netflix resilience4j is a lightweight, easy-to-use fault tolerance library inspired by Netflix Hystrix, but designed for Java 8 and functional programming.

Resilience4j provides higher-order functions (decorators) to enhance any functional interface, lambda expression or method reference, including circuit breakers, rate limiters, retries or bulkheads. Multiple decorators can be used on any functional interface, lambda expression or method reference.

Resilience4j is very lightweight, not only can be used in SpringCloud, but also the function of Rate Limit can be realized directly in your own Java program through the API of Resilience4j; this is also a point that the author agrees with; it is more convenient to support and expand; today This article focuses on how to use Resilicen4j in SpringCloud

import dependencies

Introduce the dependency on Resilience4j through pom.xml

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot</artifactId>
</dependency>

Configure resilience4j

Add the relevant configuration of resilience4j in SpringCloud to the applicaiton.yml of the project

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

In this configuration, circuitbeaker, retry, bulkhead, ratelimiter, timelimiter, thread-pool-bulkhead and other related circuit breaker and current limiting related items are respectively configured. The introduction of resilience4j-spring-boot integrates the functions of circuitbeaker, retry, bulkhead, and ratelimiter modules, and provides related implementations; we can make specific configuration changes to these related projects according to the actual needs of the business;

The development is roughly the same as using Hystrix; but pay attention to the adjustments

The feign contract can only use feign's contract, not the SpringMVC contract

The feign contract can only be used by feign, not the SpringMVC contract; so the default method cannot be used, and it must be modified by autoconfiguraiton

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

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

Use standard Feign annotations

Due to the modification of feign's contract method; therefore, the default feign annotation method cannot use SpringMVC method, and Feign's standard method must be used

// 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);
}

From the above code, we can see that in the definition interface of Feign, the standard Post/Get/Delete/Request in SpringMVC is no longer used; instead, the standard annotation RequestLine of Feign is used; it is estimated that most of the friends who have done Feign also I don’t know that this is Feign’s standard Annotation; Due to the previous step, we modified Consract and no longer use SpringMVCConstract; so Post/Get/Delete/Request, etc. Annotation in SpringMVC cannot be used; it must be replaced;

business call

In the last piece of code, we defined the remote call interface of RPC; in the service layer, we use the defined Feign interface to complete the business call;

@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);
    }
}

The code of the above business call; it is a little different from the call we often use OpenFeign; in the acquisition of the IOC of CodeManFeign; you can see the code

Commented out the way through IOC

//@Autowired

//CodeManFeign codeManFeign;

but through

@PostConstruct

public void init(){

}

In init, Resilience4jFeign.builder(decorators).target() is instantiated through FeignDecorators.builder() to instantiate this; this is also a puzzling place; why not use a method similar to Hystrix. When defining the Feign interface, we also do some binding, but manually bind through the API here; I don’t understand this: personal feeling; maybe it’s still in the product design time to make this setting more detailed Bar.

In our own project, we did the processing here. In fact, the purpose of this processing is to omit the code in Init

Define the implementation of 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";
    }
}

Through the above steps, the realization of the fuse of Resilience4j can be realized. As an aside, since our own projects use springcloud to implement microservices; and they are all based on springcloud and combined with the characteristics of the company's projects, we encapsulate our own springcloud development framework. The ioc in springcloud is a good thing, but The ioc in spring is everywhere, which not only increases the complexity of debugging and tracking problems; but also makes people worry that ioc is processed by reflection or dynamic proxy. Such an implementation really exists There are great hidden dangers; however, since most of the microservices we implement using SpringCloud are used to process business applications, the low performance of reflection is basically negligible compared to the performance bottleneck of business applications. Such hidden dangers Also hid. Of course, after learning the go language, I deeply realized the difference in the choice of design patterns between service and business;

 

conclusion

This article mainly introduces how to use Resilience4j to implement the fuse function in the development of SpringCloud microservices; you can also directly implement the fuse function in your own Java program through the API of resilience4j. To be honest, SpringCloud microservices Comparing the system with the Service Mesh system is really a comparison between a child and an adult. Is the circuit breaker a business requirement? If not, why is the microservice code incorporated into the circuit breaker development code in the development of springCloude? Be awkward; this is why Service Mesh is the future.

Guess you like

Origin blog.csdn.net/inthirties/article/details/126740552