Resilience4j

Resilience4j(绿类楞次)是Spring Cloud Greenwich版推荐的容错解决方案,相比Hystrix,Resilience4j专为java8以及函数式编程而设计。
Resilience4j主要提供了如下功能:

  1. 断流器
  2. 限流
  3. 基于信号量的隔离
  4. 缓存
  5. 限时
  6. 请求重试

基本用法

首先搭建一个简单的测试环境

断路器

Resilience4j提供了很多功能,不同的功能对应不同的依赖,可以按需添加
使用断路器,则首先添加断路器的依赖


    io.github.resilience4j
    resilience4j-circuitbreaker
    0.13.2

一个正常执行的例子:

public class ResilienceTest {
@Test
public void test(){
    //获取一个CircuitBreakerRegistry实例,可以调用ofDefaults获取一个CircuitBreakerRegister实例,也可以自定义属性
    CircuitBreakerRegistry registry = CircuitBreakerRegistry.ofDefaults();
    CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            //故障率阈值百分比,超过这个阈值,断路器就会打开
            .failureRateThreshold(50)
            //断路器保存打开的时间,在到达设置的时间之后,断路器就会进入half open状态
            .waitDurationInOpenState(Duration.ofMillis(1000))
            //当断路器处于half open状态时,环形缓冲区的大小
            .ringBufferSizeInHalfOpenState(2)
            .ringBufferSizeInClosedState(2)
            .build();
    CircuitBreakerRegistry r1 = CircuitBreakerRegistry.of(config);
    CircuitBreaker cb1 = r1.circuitBreaker("javaboy");
    CircuitBreaker cb2 = r1.circuitBreaker("javaboy2", config);
    CheckedFunction0 supplier = CircuitBreaker.decorateCheckedSupplier(cb1, () -> "hello " +
            "resilience4");
    Try result = Try.of(supplier).map(v -> v + " hello world");
    System.out.println(result.isSuccess());
    System.out.println(result.get());
}

一个出异常的断路器

@Test
public void test2(){
    CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            //故障率阈值百分比,超过这个阈值,断路器就会打开
            .failureRateThreshold(50)
            //断路器保存打开的时间,在到达设置的时间之后,断路器就会进入half open状态
            .waitDurationInOpenState(Duration.ofMillis(1000))
            //当断路器处于half open状态时,环形缓冲区的大小
            .ringBufferSizeInHalfOpenState(2)
            .ringBufferSizeInClosedState(2)
            .build();
    CircuitBreakerRegistry r1 = CircuitBreakerRegistry.of(config);
    CircuitBreaker cb1 = r1.circuitBreaker("javaboy");
    System.out.println(cb1.getState());//获取断路器的一个状态
    cb1.onError(0, new RuntimeException());
    System.out.println(cb1.getState());//获取断路器的一个状态
    cb1.onError(0, new RuntimeException());
    System.out.println(cb1.getState());//获取断路器的一个状态
    CheckedFunction0 supplier =
            CircuitBreaker.decorateCheckedSupplier(cb1, () -> "hello resilience4j");
    Try result = Try.of(supplier)
            .map(v -> v + " hello world");
    System.out.println(result.isSuccess());
    System.out.println(result.get());

}

注意,由于ringBufferSizeInClosedState的值为2,表示当有两条数据时才会去统计故障率,所以,下面的手动故障测试,至少调用两次onEorror,断路器才会打开

断路器

RateLimiter本身和前面的断路器很像。
首先添加依赖:


    io.github.resilience4j
    resilience4j-ratelimiter
    0.13.2

限流测试:

@Test
public void test3(){
    RateLimiterConfig config = RateLimiterConfig.custom()
            .limitRefreshPeriod(Duration.ofMillis(1000))
            .limitForPeriod(4)
            .timeoutDuration(Duration.ofMillis(1000))
            .build();
    RateLimiter rateLimiter = RateLimiter.of("javaboy", config);
    CheckedRunnable checkedRunnable =
            RateLimiter.decorateCheckedRunnable(rateLimiter, () -> {
                System.out.println(new Date());
            });
    Try.run(checkedRunnable)
            .andThenTry(checkedRunnable)
            .andThenTry(checkedRunnable)
            .andThenTry(checkedRunnable)
            .onFailure(t -> System.out.println(t.getMessage()));
}

请求重试

加依赖:


    io.github.resilience4j
    resilience4j-retry
    0.13.2

案例:

@Test
public void test4() {
    RetryConfig config = RetryConfig.custom()
            //重试次数
            .maxAttempts(2)
            //重试间隔
            .waitDuration(Duration.ofMillis(500))
            //重试异常
            .retryExceptions(RuntimeException.class)
            .build();
    Retry retry = Retry.of("javaboy", config);
    Retry.decorateRunnable(retry, new Runnable() {
        int count = 0;
        //开启了重试功能之后,run 方法执行时,如果抛出异常,会自动触发重试功能
        @Override
        public void run() {
            if (count++ < 3) {
                throw new RuntimeException();
            }
        }
    }).run();
}

结合微服务

Retry重试,CircuitBreaker断路器,RateLimiter限流

Retry

首先创建一个SpringBoot项目,创建时,添加enreka-client依赖,使之能够注册到eureka上。
Resilience4j_第1张图片

项目创建成功后,手动添加Resilience4j依赖


    io.github.resilience4j
    resilience4j-spring-boot2
    1.3.1
    
        
            io.github.resilience4j
            resilience4j-circuitbreaker
        
        
            io.github.resilience4j
            resilience4j-ratelimiter
        
        
            io.github.resilience4j
            resilience4j-bulkhead
        
        
            io.github.resilience4j
            resilience4j-timelimiter
        
    

resilience4j-spring-boot2 中包含了 Resilience4j 的所有功能,但是没有配置的功能无法使用,需要将之从依赖中剔除掉。
在provider中的hello方法:

@Override
public String hello() {
    String s = "hello javaboy:" + port;
    System.out.println(s);
    int i = 1 / 0;
    return s;
}

接下来,在application.yml中配置retry:

resilience4j:
  retry:
    retry-aspect-order: 399 # 表示Retry的优先级 数值越小优先级越高
    backends:
      retryA:
        maxRetryAttempts: 5 # 重试次数
        waitDuration: 500 # 重试等待时间
        exponentialBackoffMultiplier: 1.1 # 间隔乘数
        retryExceptions:
          - java.lang.RuntimeException
spring:
  application:
    name: resilience4j
server:
  port: 5000
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1111/eureka

最后创建RestTemplate和HelloService

@SpringBootApplication
public class Resilience4j2Application {

    public static void main(String[] args) {
        SpringApplication.run(Resilience4j2Application.class, args);
    }
    @Bean
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}


@Service
@Retry(name = "retryA")
public class HelloService {
    @Autowired
    RestTemplate restTemplate;

    public String hello(){
        return restTemplate.getForObject("http://localhost:1113/hello", String.class);
    }
}


@RestController
public class HelloController {
    @Autowired
    HelloService helloService;
    @GetMapping("/hello")
    public String hello() {
        return helloService.hello();
    }
}

访问localhost:5000/hello,回到控制台,可以看到打印出了5次hello javaboy:1113,也就是说重试了5次
Resilience4j_第2张图片

CircuitBreaker

首先从依赖中删除排除CircuitBreaker。
Resilience4j_第3张图片
然后在application.yml中进行配置:

resilience4j:
  retry:
    retry-aspect-order: 399 # 表示Retry的优先级 数值越小优先级越高
    backends:
      retryA:
        maxRetryAttempts: 5 # 重试次数
        waitDuration: 500 # 重试等待时间
        exponentialBackoffMultiplier: 1.1 # 间隔乘数
        retryExceptions:
          - java.lang.RuntimeException
  circuitbreaker:
    instances:
      cbA:
        ringBufferSizeInClosedState: 5
        ringBufferSizeInHalfOpenState: 3
        waitInterval: 5000
        recordExceptions:
          - org.springframework.web.client.HttpServerErrorException
    circuit-breaker-aspect-order: 398
spring:
  application:
    name: resilience4j
server:
  port: 5000
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1111/eureka

配置完成后,用@CircuitBreakder注解标记相关方法:

@Service
@CircuitBreaker(name = "cbA",fallbackMethod = "error")
public class HelloService {
    @Autowired
    RestTemplate restTemplate;

    public String hello(){
        return restTemplate.getForObject("http://localhost:1113/hello", String.class);
    }

    public String error(Throwable t){
        return "error";
    }
}

@CircuitBreaker 注解中的 name 属性用来指定 circuitbreaker 配置,fallbackMethod 属性用来指定服务降级的方法,需要注意的是,服务降级方法中,要添加异常参数。
测试:
Resilience4j_第4张图片

RateLimiter

RateLimiter作为限流工具,主要在服务端使用,用来保护服务端的接口

首先在provider中添加RateLimiter依赖


    io.github.resilience4j
    resilience4j-spring-boot2
    1.3.1
    
        
            io.github.resilience4j
            resilience4j-circuitbreaker
        
        
            io.github.resilience4j
            resilience4j-bulkhead
        
        
            io.github.resilience4j
            resilience4j-timelimiter
        
    

接下来,在 provider 的 application.properties 配置文件中,去配置 RateLimiter

# 这里配置每秒钟处理一个请求
resilience4j.ratelimiter.limiters.rlA.limit-for-period=1
resilience4j.ratelimiter.limiters.rlA.limit-refresh-period=1s
resilience4j.ratelimiter.limiters.rlA.timeout-duration=1s

为了查看请求效果,在provider的HelloController中打印每一个请求的时间

@Override
@RateLimiter(name = "rlA")
public String hello() {
    String s = "hello javaboy:" + port;
    System.out.println(new Date());
    return s;
}

这里通过 @RateLimiter 注解来标记该接口限流。
配置完成后,重启 provider。
然后,在客户端模拟多个请求,查看限流效果:

@GetMapping("/hello")
public String hello() {
    for (int i = 0; i < 5; i++) {
        restTemplate.getForObject("http://localhost:1113/hello", String.class);
    }
    return "success";
}

效果图:
Resilience4j_第5张图片

服务监控

微服务由于服务数量众多,所以出故障的概率很大,这种时候不能单纯的依靠人肉运维。早期的spring cloud中,服务监控主要使用Hystrix Dashboard,集群数据库监控使用Turbine.
在Greenwich版本中,官方建议监控工具使用Micrometer。
Micrometer:

  1. 提供了度量指标,例如timers,counters
  2. 一揽子开箱即用的解决方案,例如缓存,类加载器,垃圾收集等等。
    新建一个spring boot项目,添加web和Actuator依赖。项目创建成功后,添加如下配置,开启所有端点:
management.endpoints.web.exposure.include=*

然后就可以在浏览器查看项目的各项运行数据,但是这些数据都是JSON格式
Resilience4j_第6张图片
我们需要一个可视化工具来展示这些JSON数据,这里主要和大家介绍Prometheus

Prometheus(普罗米修斯)

安装

wget
https://github.com/prometheus/prometheus/releases/download/v2.16.0/prometheus-
2.16.0.linux-amd64.tar.gz
tar -zxvf prometheus-2.16.0.linux-amd64.tar.gz

解压完成后,配置一下数据路径和要监控的服务地址:

cd prometheus-2.16.0.linux-amd64/
vi prometheus.yml

修改 prometheus.yml 配置文件,主要改两个地方,一个是数据接口,另一个是服务地址:
Resilience4j_第7张图片
接下来,将 Prometheus 整合到 Spring Boot 项目中。
首先加依赖:


io.micrometer
micrometer-registry-prometheus

然后在 application.properties 配置中,添加 Prometheus 配置:

management.endpoints.web.exposure.include=*
management.endpoint.prometheus.enabled=true
management.metrics.export.prometheus.enabled=true
management.endpoint.metrics.enabled=true

接下来启动 Prometheus。
启动命令:

./prometheus --config.file=prometheus.yml

启动成功后,浏览器输入 http://192.168.91.128:9090 查看 Prometheus 数据信息。

Grafana

Grafana:https://grafana.com/grafana/download?platform=linux

你可能感兴趣的:(Resilience4j)