server

github:https://github.com/ln0491/resilience4j-springboot

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
  • 配置
spring.application.name=eureka server
server.port=7979

eureka.instance.hostname=eureka-server
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true
  • 启动
package com.ghgcn.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }

}

provider
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

  • properties
spring.application.name=rj-provider
server.port=7981

eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.serviceUrl.defaultZone=http://localhost:7979/eureka
#默认就是true
spring.cloud.loadbalancer.retry.enabled=true

@RestController
public class HelloContrtoller {


    @GetMapping("/hello")
    public  String hell(String name){
        String s= "hello "+name +" !";
        System.out.println("s "+s+" "+new Date());
 		int i = 1/0;
        return s;
    }
}
@SpringBootApplication
public class R4jProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(R4jProviderApplication.class, args);
    }

}

comsumer
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>0.14.1</version>
        </dependency>
server.port=7877
spring.application.name=r4j-consumer

eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true
eureka.client.serviceUrl.defaultZone=http://localhost:7979/eureka
resilience4j.retry.retry-aspect-order=399
resilience4j.retry.backends.retryBackendA.max-retry-attempts=3
resilience4j.retry.backends.retryBackendA.wait-duration=600
resilience4j.retry.backends.retryBackendA.event-consumer-buffer-size=1
resilience4j.retry.backends.retryBackendA.enable-exponential-backoff=true
resilience4j.retry.backends.retryBackendA.exponential-backoff-multiplier=2
resilience4j.retry.backends.retryBackendA.enable-randomized-wait=false
resilience4j.retry.backends.retryBackendA.randomized-wait-factor=2
resilience4j.retry.backends.retryBackendA.retry-exception-predicate=com.ghgcn.r4jconsumer.predicate.RecordFailurePredicate
resilience4j.retry.backends.retryBackendA.retry-exceptions=java.io.IOException,java.lang.ArithmeticException
resilience4j.retry.backends.retryBackendA.ignore-exceptions=com.ghgcn.r4jconsumer.exception.IgnoredException



  • retryAspectOrder 表示 Retry 的一个优先级。默认情况下, Retry 的优先级高于 bulkhead 、 Circuit breaker 以及 rateLimiter ,即 Retry 会先于另外三个执行。 Retry、 bulkhead 、 Circuit breaker 以及 rateLimiter 的优先级数值默认分别是 Integer.MAX_VALUE-3、Integer.MAX_VALUE-2、Integer.MAX_VALUE-1 以及 Integer.MAX_VALUE ,即数值越小,优先级越高;
  • backends 属性中我们可以配置不同的 Retry 策略,给不同的策略分别取一个名字, retryBackendA 就是一个 Retry 策略的名字。在 Java 代码中,我们将直接通过指定 Retry 策略的名字来使用某一种 Retry 方案;
  • maxRetryAttempts 表示最大重试次数;
  • waitDuration 表示下一次重试等待时间,最小为100 ms ;
  • eventConsumerBufferSize 表示重试事件缓冲区大小;
  • enableExponentialBackoff 表示是否开启指数退避抖动算法,当一次调用失败后,如果在相同的时间间隔内发起重试,有可能发生连续的调用失败,因此可以开启指数退避抖动算法;
  • exponentialBackoffMultiplier 表示时间间隔乘数;
  • enableRandomizedWait 表示下次重试的时间间隔是否随机, enableRandomizedWait 和 enableExponentialBackoff 默认为 false ,并且这两个不可以同时开启;
  • retryExceptionPredicate 类似于我们上文所说的什么样的异常会被认定为请求失败,这里的RecordFailurePredicate是一个自定义的类;
  • retryExceptions 表示需要重试的异常;
  • ignoreExceptions 表示忽略的异常。
public class RecordFailurePredicate implements Predicate<Throwable> {
    @Override
    public boolean test(Throwable throwable) {
        return true;
    }
}
public class IgnoredException extends  Exception {
}

使用
@RestController
@Retry(name = "retryBackendA")
public class HelloController {
    @Autowired
    RestTemplate restTemplate;

    @Autowired
    DiscoveryClient discoveryClient;


    @GetMapping("/hello")
    public  String hell(String name){
        List<ServiceInstance> list = discoveryClient.getInstances("rj-provider");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        System.err.println(host);
        int port = instance.getPort();
        System.err.println(port);
        String serviceId = instance.getServiceId();

        //String s = restTemplate.getForObject("http://" + host + ":" + port + "/hello?name={1}", String.class, name);
        String s = restTemplate.getForObject("http://"+serviceId+"/hello?name={1}",String.class, name);
        return s;

    }
  }
编程式
@RestController
public class UseHelloController {
    @Autowired
    HelloService helloService;
    @GetMapping("/hello2")
    public String hello2(String name) {
        RetryConfig config = RetryConfig.custom()
                .maxAttempts(3)
                .waitDuration(Duration.ofMillis(5000))
                .build();
        Retry retry = Retry.of("id", config);
        Try<String> result = Try.ofSupplier(Retry.decorateSupplier(retry, () -> helloService.hello(name)));
        return result.get();
    }
}
CircuitBreaker

在这里插入图片描述

  • backendA 是断路器策略的命名,和 Retry 类似,一会也是通过注解来引用这个策略;
  • ringBufferSizeInClosedState 表示断路器关闭状态下,环形缓冲区的大小;
  • ringBufferSizeInHalfOpenState 表示断路器处于 HalfOpen 状态下,环形缓冲区的大小;
  • waitInterval 表示断路器从 open 切换到 half closed 状态时,需要保持的时间;
  • failureRateThreshold 表示故障率阈值百分比,超过这个阈值,断路器就会打开;
  • eventConsumerBufferSize 表示事件缓冲区大小;
  • registerHealthIndicator 表示开启健康检测。
@Service
@CircuitBreaker(name = "backendA")
public class HelloServiceCircuitBreaker {
    @Autowired
    RestTemplate restTemplate;

    public String hello(String name) {
        return restTemplate.getForObject("http://provider/hello?name={1}", String.class, name);
    }
}
编程式
public String hello2(String name) {
    CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofMillis(1000))
            .ringBufferSizeInHalfOpenState(20)
            .ringBufferSizeInClosedState(20)
            .build();
    io.github.resilience4j.circuitbreaker.CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("backendA", circuitBreakerConfig);
    Try<String> supplier = Try.ofSupplier(io.github.resilience4j.circuitbreaker.CircuitBreaker
            .decorateSupplier(circuitBreaker,
                    () -> restTemplate.getForObject("http://provider/hello?name={1}", String.class, name)))
            .recover(Exception.class, "有异常,访问失败!");
    return supplier.get();
}

这里的 hello2 方法去访问 provider 中的接口。接口调用失败后, consumer 中自动进行服务降级,最终返回字符串为有异常,访问失败

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐