文章摘要
GPT 4
此内容根据文章生成,仅用于文章内容的解释与总结
投诉

resilience4j

在 Spring Cloud 中使用 Resilience4J 断路器进行配置非常直观。下面我会提供一个简单的示例,展示如何在 Spring Boot 应用程序中配置 Resilience4J 断路器。

一、circuitbreaker(断路器)

1. 添加依赖

首先,确保你的项目中包含了必要的依赖。对于非响应式应用程序,你需要添加以下依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
<version>${spring-cloud.version}</version>
</dependency>

对于响应式应用程序,则需要使用:

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
<version>${spring-cloud.version}</version>
</dependency>

同时确保添加 Spring Cloud 的依赖管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<properties>
<spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>

2. 配置 Resilience4J

接下来,在 application.ymlapplication.properties 文件中添加 Resilience4J 的配置。这里以 application.yml 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
spring:
cloud:
gateway:
routes:
- id: product-service
uri: lb://product-service
predicates:
- Path=/products/**
filters:
- name: CircuitBreaker
args:
name: myService
fallbackUri: forward:/fallback
resilience4j:
circuitbreaker:
configs:
default: # 定义默认配置
failureRateThreshold: 50 # 当失败率达到50%时打开断路器
minimumNumberOfCalls: 5 # 最小请求数量
waitDurationInOpenState: 5s # 断路器处于打开状态等待重试的时间
permittedNumberOfCallsInHalfOpenState: 2 # 半开状态下允许尝试的请求次数
automaticTransitionFromOpenToHalfOpenEnabled: true # 是否自动从打开转为半开状态
eventConsumerBufferSize: 10 # 缓冲区大小
myCustomConfig: # 定义自定义配置
failureRateThreshold: 60
minimumNumberOfCalls: 10
waitDurationInOpenState: 10s
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: false
eventConsumerBufferSize: 20
instances:
myService: # 应用到特定的服务实例
baseConfig: default
registerHealthIndicator: true # 注册健康指标
# 可以覆盖上面的默认配置
# failureRateThreshold: 70
# minimumNumberOfCalls: 15
# waitDurationInOpenState: 15s
# permittedNumberOfCallsInHalfOpenState: 4
# automaticTransitionFromOpenToHalfOpenEnabled: true
# eventConsumerBufferSize: 25
# 日志记录配置
logging:
level:
io.github.resilience4j: DEBUG

3. 使用断路器

你可以在 Feign 客户端或 OpenFeign 中使用 Resilience4J 断路器,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@FeignClient(name = "myService", configuration = MyFeignConfig.class)
public interface MyFeignClient {
@GetMapping("/data")
String getData();
}

@Configuration
public class MyFeignConfig {
@Bean
public Feign.Builder feignBuilder() {
return Feign.builder()
.client(new OkHttpClient())
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.contract(new SpringMvcContract())
.errorDecoder((status, response) -> new RuntimeException("Error: " + status))
.target(MyFeignClient.class, "http://localhost:8080");

// 使用 Resilience4J 断路器
return feignBuilder().requestInterceptor(new Resilience4JCircuitBreakerRequestInterceptor("myService"));
}
}

4. 日志记录

在上面的配置中,我们还设置了日志级别,这样可以查看 Resilience4J 的日志输出。

5. 测试

确保你的服务能够正常启动,并且可以通过断路器调用远程服务。你可以模拟远程服务的故障来测试断路器是否能够正确地打开和关闭。

以上步骤应该可以帮助你在 Spring Cloud 项目中配置 Resilience4J 断路器。如果有任何具体的需求或者遇到问题,请随时告诉我。

二、Rate Limiter(限流器)

在 Spring Cloud 中使用 Resilience4J 进行限流配置是非常直观的。下面我将向您展示如何在 Spring Boot 应用程序中配置 Resilience4J 的 Rate Limiter(限流器)。

1. 配置 Resilience4J

接下来,在 application.ymlapplication.properties 文件中添加 Resilience4J 的配置。这里以 application.yml 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
resilience4j:
ratelimiter:
configs:
default:
limitForPeriod: 10 # 在限流周期内允许的最大请求数量
limitRefreshPeriod: 5s # 限流周期时长
timeoutDuration: 0ms # 超时持续时间,0 表示不限制
myCustomConfig: # 自定义配置
limitForPeriod: 20
limitRefreshPeriod: 10s
timeoutDuration: 500ms
#rate-limiter:
instances:
myService: # 应用到特定的服务实例
baseConfig: default
# 可以覆盖上面的默认配置
# limitForPeriod: 15
# limitRefreshPeriod: 15s
# timeoutDuration: 1s

2. 使用限流器

要在代码中使用限流器,你可以通过注入 RateLimiter 来实现。下面是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RateLimiterController {

@Autowired
private RateLimiterRegistry rateLimiterRegistry;

@GetMapping("/data")
public String getData() {
// 获取名为 "myService" 的 RateLimiter 实例
RateLimiter rateLimiter = rateLimiterRegistry.rateLimiter("myService");
// 尝试获取权限
boolean allowed = rateLimiter.acquirePermission();
if (!allowed) {
throw new RuntimeException("Rate limit exceeded");
}
// 在这里执行业务逻辑
return "Data";
}
}

3. 请求限流

​ 在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击。

​ 一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如 nginx 的 limit_conn 模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如 Guava 的 RateLimiter、nginx 的 limit_req 模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制 MQ 的消费速率。另外还可以根据网络连接数、网络流量、CPU 或内存负载等来限流。

  • 限流算法
  1. 计数器

​ 简单的做法是维护一个单位时间内的 计数器,每次请求计数器加1,当单位时间内计数器累加到大于设定的阈值,则之后的请求都被拒绝,直到单位时间已经过去,再将 计数器 重置为零。此方式有个弊端:如果在单位时间1s内允许100个请求,在10ms已经通过了100个请求,那后面的990ms,只能眼巴巴的把请求拒绝,我们把这种现象称为“突刺现象”。

​ 常用的更平滑的限流算法有两种:漏桶算法令牌桶算法。下面介绍下二者。

  1. 漏桶算法

​ 漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。

​ 可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。

  1. 令牌桶算法

​ 令牌桶算法 和漏桶算法 效果一样但方向相反的算法,更加容易理解。随着时间流逝,系统会按恒定 1/QPS 时间间隔(如果 QPS=100,则间隔是 10ms)往桶里加入 Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了。新请求来临时,会各自拿走一个 Token,如果没有 Token 可拿了就阻塞或者拒绝服务。

​ 令牌桶的另外一个好处是可以方便的改变速度。一旦需要提高速率,则按需提高放入桶中的令牌的速率。一般会定时(比如 100 毫秒)往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量。

  • 限流实现

​ 在 Spring Cloud Gateway 上实现限流是个不错的选择,只需要编写一个过滤器就可以了。有了前边过滤器的基础,写起来很轻松。

Spring Cloud Gateway 已经内置了一个RequestRateLimiterGatewayFilterFactory,我们可以直接使用。

  • pom.xml
1
2
3
4
5
6
7
8
9
<!-- 熔断(断路)器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifatId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
  • application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server:
port: 8080
spring:
cloud:
gateway:
routes:
- id: limit_route
uri: http://httpbin.org:80/get
predicates:
- After=2019-02-26T00:00:00+08:00[Asia/Shanghai]
filters:
- name: RequestRateLimiter
args:
key-resolver: '#{@hostAddrKeyResolver}'
redis-rate-limiter:
replenishRate: 1
burstCapacity: 3
default-filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@hostAddrKeyResolver}" # SpEL,限流解析器的 Bean 对象的名字
redis-rate-limiter:
replenishRate: 10
burstCapacity: 20
application:
name: gateway-limiter
redis:
host: localhost
port: 6379
database: 0

在上面的配置文件,配置了 redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:

  • burstCapacity:令牌桶总容量。
  • replenishRate:令牌桶每秒填充平均速率。
  • key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

IP限流

获取请求用户ip作为限流key。

1
2
3
4
@Bean //Bean Name: hostAddrKeyResolver
public KeyResolver hostAddrKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

用户限流

获取请求用户id作为限流key。

1
2
3
4
@Bean //Bean Name: userKeyResolver
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}

接口限流

获取请求地址的uri作为限流key。

1
2
3
4
@Bean //Bean Name: apiKeyResolver
KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}

3. 日志记录

在上面的配置中,我们还可以设置日志级别,以便查看 Resilience4J 的日志输出:

1
2
3
logging:
level:
io.github.resilience4j: DEBUG

4. 测试

确保你的服务能够正常启动,并且限流规则生效。你可以尝试发送多个请求来测试限流器是否能够阻止超出配额的请求。

以上步骤应该可以帮助你在 Spring Cloud 项目中配置 Resilience4J 的限流器。如果有任何具体的需求或者遇到问题,请随时告诉我。

三、Retry重试

Spring Cloud Gateway 可以与 Resilience4j 集成来实现请求级别的重试功能。为了在 Spring Cloud Gateway 中使用 Resilience4j 的重试功能,你需要配置 Resilience4j 和相应的过滤器。

以下是如何在 Spring Cloud Gateway 中配置默认过滤器(DefaultFilter)以启用 Resilience4j 重试功能的步骤和示例。

添加依赖

确保你的项目中包含了 Spring Cloud Gateway 和 Resilience4j 的相关依赖。你可能需要添加如下 Maven 依赖:

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-cloud-gateway</artifactId>
<version>${resilience4j.version}</version>
</dependency>

其中 ${resilience4j.version} 应该替换为 Resilience4j 的实际版本号。

配置 Resilience4j

接下来,在你的应用配置文件(如 application.ymlapplication.properties)中配置 Resilience4j 的重试策略:

1
2
3
4
5
6
resilience4j.retry:
instances:
default:
maxRetryAttempts: 3
waitDuration: 100ms
exponentialBackoffMultiplier: 2

这里配置了一个名为 default 的重试实例,它将应用于所有未指定特定重试策略的服务路由。

配置 Spring Cloud Gateway

然后,你需要在 Spring Cloud Gateway 的配置中启用 Resilience4j 的重试过滤器。这可以通过全局配置或者针对特定路由进行配置。

全局配置

如果你想让所有的路由都使用默认的重试策略,可以在全局配置中启用 Resilience4j Retry 过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods: "*"
default-filters:
- name: Retry
args:
RejectedExecutionHandler: SimpleAsyncTaskExecutor
Resilience4JConfigName: default

这里的 Resilience4JConfigName 设置为 default,意味着使用上面配置的 default 实例。

针对特定路由配置

如果你想只为某些路由配置重试策略,可以在路由配置中单独指定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
cloud:
gateway:
routes:
- id: service1
uri: lb://service1
predicates:
- Path=/api/service1/**
filters:
- name: Retry
args:
RejectedExecutionHandler: SimpleAsyncTaskExecutor
Resilience4JConfigName: service1
default-filters:
- name: Retry
args:
RejectedExecutionHandler: SimpleAsyncTaskExecutor
Resilience4JConfigName: default

在这个示例中,路由 service1 使用了一个名为 service1 的重试配置实例,而其他未指定的路由将使用默认的 default 配置。

确保在应用启动类中也启用了 Resilience4j 的自动配置:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableWebFlux
@EnableCircuitBreaker
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

以上配置将允许 Spring Cloud Gateway 使用 Resilience4j 的重试机制处理请求。你可以根据自己的需求调整配置参数。