Bulkhead(做并发的控制)
目的
一个仓的意思,有一种模式,叫隔仓模式,在这里帮我们去做一个保护。
- 防止下游依赖被大量的并发请求冲击
以上一篇的例子为例,只能并发5个请求过去,剩下的要么就排队 要么就失败 - 防止发生连环故障
假设 原来一个需要一两毫秒的数据库操作请求 因为 某些原因 产生故障 需要一两秒 上游的服务有大量请求去访问它 这就给它增加负担 甚至 将下游的服务本身的线程池塞满 造成连环的反应 用了Bulkhead之后 假如 我们对下游只有5个并发 那 不管下游什么情况 上游只有5个并发过去 剩下的 在超过配置的等待时间过去之后 它自然就不会往下走 也不会往下游发起更多的请求 下游也不会被上游的请求 导致线程池塞满
用法
- 配置 BulkheadRegistry / BulkheadConfig
- 使用 @Bulkhead(name = “名称”) 注释 配置Bulkhead的名称
配置
- 在 BulkheadProperties 中 可以配置以下属性
• resilience4j.bulkhead.backends.名称
• max-concurrent-call
最大的并发调用的请求数量
• max-wait-time
最大的等待时间
实例
分为 bulkhead-customer-service 与 consul-waiter-service 两部分组成
bulkhead-customer-service
需要修改的代码
CustomerController
@RestController
@RequestMapping("/customer")
@Slf4j
public class CustomerController {
@Autowired
private CoffeeService coffeeService;
@Autowired
private CoffeeOrderService coffeeOrderService;
private CircuitBreaker circuitBreaker;
private Bulkhead bulkhead;
//使用代码实现
public CustomerController(CircuitBreakerRegistry circuitBreakerRegistry,
BulkheadRegistry bulkheadRegistry) {
circuitBreaker = circuitBreakerRegistry.circuitBreaker("menu");
bulkhead = bulkheadRegistry.bulkhead("menu");
}
@GetMapping("/menu")
public List<Coffee> readMenu() {
return Try.ofSupplier(
Bulkhead.decorateSupplier(bulkhead,
CircuitBreaker.decorateSupplier(circuitBreaker,
() -> coffeeService.getAll())))
.recover(CircuitBreakerOpenException.class, Collections.emptyList())
.recover(BulkheadFullException.class, Collections.emptyList())
.get();//套了两层
}
//使用注解实现
@PostMapping("/order")
@io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker(name = "order")
@io.github.resilience4j.bulkhead.annotation.Bulkhead(name = "order")
public CoffeeOrder createOrder() {
NewOrderRequest orderRequest = NewOrderRequest.builder()
.customer("Li Lei")
.items(Arrays.asList("capuccino"))
.build();
CoffeeOrder order = coffeeOrderService.create(orderRequest);
log.info("Order ID: {}", order != null ? order.getId() : "-");
return order;
}
}
application.properties
server.port=8090
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
feign.client.config.default.connect-timeout=500
feign.client.config.default.read-timeout=500
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.prefer-ip-address=true
resilience4j.circuitbreaker.backends.order.failure-rate-threshold=50
resilience4j.circuitbreaker.backends.order.wait-duration-in-open-state=5000
resilience4j.circuitbreaker.backends.order.ring-buffer-size-in-closed-state=5
resilience4j.circuitbreaker.backends.order.ring-buffer-size-in-half-open-state=3
resilience4j.circuitbreaker.backends.order.event-consumer-buffer-size=10
resilience4j.circuitbreaker.backends.menu.failure-rate-threshold=50
resilience4j.circuitbreaker.backends.menu.wait-duration-in-open-state=5000
resilience4j.circuitbreaker.backends.menu.ring-buffer-size-in-closed-state=5
resilience4j.circuitbreaker.backends.menu.ring-buffer-size-in-half-open-state=3
resilience4j.circuitbreaker.backends.menu.event-consumer-buffer-size=10
resilience4j.bulkhead.backends.order.max-concurrent-call=1
#最大并发调用数量
resilience4j.bulkhead.backends.order.max-wait-time=5
#最大等待时间
resilience4j.bulkhead.backends.menu.max-concurrent-call=5
#最大并发调用数量
resilience4j.bulkhead.backends.menu.max-wait-time=5
#最大等待时间
pom文件
<dependencies>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>0.14.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
consul-waiter-service
引至:https://blog.csdn.net/weixin_43790623/article/details/104545593
结果分析
运行 WaiterServiceApplication 与 CustomerServiceApplication
在postman中 输入 http://localhost:8090/customer/order 和 http://localhost:8090/customer/menu 查看 程序是否正常
使用 apache ab 去做一个并发的测试 我们可以看到每个请求都是成功的 没有失败的请求 可以看到全部请求都是在305ms之内做完的
安装教程:https://blog.csdn.net/qq_26565861/article/details/81198303
D:\Apache24\bin>ab -c 5 -n 10 http://localhost:8090/customer/menu
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient).....done
Server Software:
Server Hostname: localhost
Server Port: 8090
Document Path: /customer/menu
Document Length: 642 bytes
##页面数据/返回的数据量
Concurrency Level: 5
##并发数
Time taken for tests: 0.349 seconds
##共使用了多少时间
Complete requests: 10
##请求数
Failed requests: 0
Total transferred: 7610 bytes
##总共传输字节数,包含http的头信息等
HTML transferred: 6420 bytes
##html字节数,实际的页面传递字节数
Requests per second: 28.66 [#/sec] (mean)
##每秒多少请求,这个是非常重要的参数数值,服务器的吞吐量
Time per request: 174.455 [ms] (mean)
##用户平均请求等待时间
Time per request: 34.891 [ms] (mean, across all concurrent requests)
##服务器平均处理时间,也就是服务器吞吐量的倒数
Transfer rate: 21.30 [Kbytes/sec] received
##每秒获取的数据长度
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.5 1 1
##连接的最小时间,平均值,中值,最大值
Processing: 37 149 90.9 140 305
##处理时间
Waiting: 36 133 80.4 122 303
##等待时间
Total: 38 150 90.8 140 305
合计时间
Percentage of the requests served within a certain time (ms)
50% 140
66% 161
75% 217
80% 272
90% 305
95% 305
98% 305
99% 305
100% 305 (longest request)
我们现在将它变为10个并发 20个请求 ab -c 10 -n 10 http://localhost:8090/customer/menu
我们可以看到 有3个请求是失败的 这些请求等待了5ms之后 失败了
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient).....done
Server Software:
Server Hostname: localhost
Server Port: 8090
Document Path: /customer/menu
Document Length: 642 bytes
Concurrency Level: 10
Time taken for tests: 0.508 seconds
Complete requests: 20
Failed requests: 3
(Connect: 0, Receive: 0, Length: 3, Exceptions: 0)
Total transferred: 13300 bytes
HTML transferred: 10920 bytes
Requests per second: 39.37 [#/sec] (mean)
Time per request: 253.997 [ms] (mean)
Time per request: 25.400 [ms] (mean, across all concurrent requests)
Transfer rate: 25.57 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.5 0 1
Processing: 17 194 97.0 185 383
Waiting: 11 185 96.9 184 382
Total: 18 195 96.9 186 383
Percentage of the requests served within a certain time (ms)
50% 186
66% 214
75% 282
80% 283
90% 380
95% 383
98% 383
99% 383
100% 383 (longest request)
进入 http://localhost:8090/actuator/circuitbreakerevents 去看一下 失败的请求
Bulkhead 自动配置的源代码
BulkheadAutoConfiguration
@Configuration
@ConditionalOnClass({Bulkhead.class})
@EnableConfigurationProperties({BulkheadProperties.class})
@Import({BulkheadConfiguration.class}) //import了BulkheadConfiguration
@AutoConfigureBefore({EndpointAutoConfiguration.class})
public class BulkheadAutoConfiguration {
public BulkheadAutoConfiguration() {
}
//注册了bulkheadEndpoint和bulkheadEventsEndpoint
@Bean
@ConditionalOnEnabledEndpoint
public BulkheadEndpoint bulkheadEndpoint(BulkheadRegistry bulkheadRegistry) {
return new BulkheadEndpoint(bulkheadRegistry);
}
@Bean
@ConditionalOnEnabledEndpoint
public BulkheadEventsEndpoint bulkheadEventsEndpoint(EventConsumerRegistry<BulkheadEvent> eventConsumerRegistry) {
return new BulkheadEventsEndpoint(eventConsumerRegistry);
}
}
BulkheadConfiguration
@Configuration
public class BulkheadConfiguration {
public BulkheadConfiguration() {
}
@Bean
public BulkheadRegistry bulkheadRegistry(BulkheadConfigurationProperties bulkheadConfigurationProperties, EventConsumerRegistry<BulkheadEvent> bulkheadEventConsumerRegistry) {
BulkheadRegistry bulkheadRegistry = BulkheadRegistry.ofDefaults();
bulkheadConfigurationProperties.getBackends().forEach((name, properties) -> {
//根据配置的 properties 去创建了对应的Backends的一个Bulkhead
BulkheadConfig bulkheadConfig = bulkheadConfigurationProperties.createBulkheadConfig(name);
Bulkhead bulkhead = bulkheadRegistry.bulkhead(name, bulkheadConfig);
bulkhead.getEventPublisher().onEvent(bulkheadEventConsumerRegistry.createEventConsumer(name, properties.getEventConsumerBufferSize()));
});
return bulkheadRegistry;
}
@Bean//aop相关的拦截器
public BulkheadAspect bulkheadAspect(BulkheadConfigurationProperties bulkheadConfigurationProperties, BulkheadRegistry bulkheadRegistry, @Autowired(required = false) List<BulkheadAspectExt> bulkHeadAspectExtList) {
return new BulkheadAspect(bulkheadConfigurationProperties, bulkheadRegistry, bulkHeadAspectExtList);
}
@Bean
@Conditional({RxJava2OnClasspathCondition.class})
public RxJava2BulkheadAspectExt rxJava2BulkHeadAspectExt() {
return new RxJava2BulkheadAspectExt();
}
@Bean
@Conditional({ReactorOnClasspathCondition.class})
public ReactorBulkheadAspectExt reactorBulkHeadAspectExt() {
return new ReactorBulkheadAspectExt();
}
@Bean
public EventConsumerRegistry<BulkheadEvent> bulkheadEventConsumerRegistry() {
return new DefaultEventConsumerRegistry();
}
}
BulkheadConfigurationProperties
public class BulkheadConfigurationProperties {
private int bulkheadAspectOrder = 2147483645;
private Map<String, BulkheadConfigurationProperties.BackendProperties> backends = new HashMap();
public BulkheadConfigurationProperties() {
}
public int getBulkheadAspectOrder() {
return this.bulkheadAspectOrder;
}
public void setBulkheadAspectOrder(int bulkheadAspectOrder) {
this.bulkheadAspectOrder = bulkheadAspectOrder;
}
private BulkheadConfigurationProperties.BackendProperties getBackendProperties(String backend) {
return (BulkheadConfigurationProperties.BackendProperties)this.backends.get(backend);
}
public BulkheadConfig createBulkheadConfig(String backend) {
return this.createBulkheadConfig(this.getBackendProperties(backend));
}
private BulkheadConfig createBulkheadConfig(BulkheadConfigurationProperties.BackendProperties backendProperties) {
return this.buildBulkheadConfig(backendProperties).build();
}
public Builder buildBulkheadConfig(BulkheadConfigurationProperties.BackendProperties properties) {
if (properties == null) {
return new Builder();
} else {
Builder builder = BulkheadConfig.custom();
if (properties.getMaxConcurrentCall() != null) {
builder.maxConcurrentCalls(properties.getMaxConcurrentCall());
}
if (properties.getMaxWaitTime() != null) {
builder.maxWaitTime(properties.getMaxWaitTime());
}
return builder;
}
}
public Map<String, BulkheadConfigurationProperties.BackendProperties> getBackends() {
return this.backends;
}
public static class BackendProperties {
//对应了配置项
@Min(1L)
private Integer maxConcurrentCall;
//并发的调用量
@Min(0L)
private Long maxWaitTime;
//最大等待时间
@Min(1L)
private Integer eventConsumerBufferSize = 100;
public BackendProperties() {
}
public Integer getMaxConcurrentCall() {
return this.maxConcurrentCall;
}
public void setMaxConcurrentCall(Integer maxConcurrentCall) {
this.maxConcurrentCall = maxConcurrentCall;
}
public Long getMaxWaitTime() {
return this.maxWaitTime;
}
public void setMaxWaitTime(Long maxWaitTime) {
this.maxWaitTime = maxWaitTime;
}
public Integer getEventConsumerBufferSize() {
return this.eventConsumerBufferSize;
}
public void setEventConsumerBufferSize(Integer eventConsumerBufferSize) {
this.eventConsumerBufferSize = eventConsumerBufferSize;
}
}
}
RateLimiter(一个时间段内接受多少请求)
目的
- 限制特定时间段内的执行次数的保护,超过这个次数之后,它会做一个等待,超过等待时间,就直接报错,不做后面的调用
用法
- 配置 RateLimiterRegistry / RateLimiterConfig
- 使用 @RateLimiter(name = “名称”) 注解
配置
- 在 RateLimiterProperties 可以配置以下配置
• resilience4j.ratelimiter.limiters.名称
• limit-for-period
一个时间段内 限定调用的次数
• limit-refresh-period-in-millis
这个时间段多久刷新一下
• timeout-in-millis
超时的配置,使用都是的毫秒
实例
由两部分组成 bulkhead-customer-service 与 ratelimiter-waiter-service
bulkhead-customer-service
如上
ratelimiter-waiter-service
需要修改的代码
CoffeeController
@RestController
@RequestMapping("/coffee")
@RateLimiter(name = "coffee")//使用注解做限流
@Slf4j
public class CoffeeController {
@Autowired
private CoffeeService coffeeService;
@PostMapping(path = "/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public Coffee addCoffeeWithoutBindingResult(@Valid NewCoffeeRequest newCoffee) {
return coffeeService.saveCoffee(newCoffee.getName(), newCoffee.getPrice());
}
@PostMapping(path = "/", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public Coffee addJsonCoffeeWithoutBindingResult(@Valid @RequestBody NewCoffeeRequest newCoffee) {
return coffeeService.saveCoffee(newCoffee.getName(), newCoffee.getPrice());
}
@PostMapping(path = "/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public List<Coffee> batchAddCoffee(@RequestParam("file") MultipartFile file) {
List<Coffee> coffees = new ArrayList<>();
if (!file.isEmpty()) {
BufferedReader reader = null;
try {
reader = new BufferedReader(
new InputStreamReader(file.getInputStream()));
String str;
while ((str = reader.readLine()) != null) {
String[] arr = StringUtils.split(str, " ");
if (arr != null && arr.length == 2) {
coffees.add(coffeeService.saveCoffee(arr[0],
Money.of(CurrencyUnit.of("CNY"),
NumberUtils.createBigDecimal(arr[1]))));
}
}
} catch (IOException e) {
log.error("exception", e);
} finally {
IOUtils.closeQuietly(reader);
}
}
return coffees;
}
@GetMapping(path = "/", params = "!name")
public List<Coffee> getAll() {
return coffeeService.getAllCoffee();
}
@GetMapping("/{id}")
public Coffee getById(@PathVariable Long id) {
Coffee coffee = coffeeService.getCoffee(id);
log.info("Coffee {}:", coffee);
return coffee;
}
@GetMapping(path = "/", params = "name")
public Coffee getByName(@RequestParam String name) {
return coffeeService.getCoffee(name);
}
}
CoffeeOrderController
@RestController
@RequestMapping("/order")
@Slf4j
public class CoffeeOrderController {
@Autowired
private CoffeeOrderService orderService;
@Autowired
private CoffeeService coffeeService;
private RateLimiter rateLimiter;
public CoffeeOrderController(RateLimiterRegistry rateLimiterRegistry) {
rateLimiter = rateLimiterRegistry.rateLimiter("order");//使用代码实现
}
@GetMapping("/{id}")
public CoffeeOrder getOrder(@PathVariable("id") Long id) {
CoffeeOrder order = null;
try {
order = rateLimiter.executeSupplier(() -> orderService.get(id));//封装orderService.get(id)
log.info("Get Order: {}", order);
} catch(RequestNotPermitted e) {
log.warn("Request Not Permitted! {}", e.getMessage());//遇到异常 返回日志
}
return order;
}
@PostMapping(path = "/", consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseStatus(HttpStatus.CREATED)
@io.github.resilience4j.ratelimiter.annotation.RateLimiter(name = "order")//使用注解
public CoffeeOrder create(@RequestBody NewOrderRequest newOrder) {
log.info("Receive new Order {}", newOrder);
Coffee[] coffeeList = coffeeService.getCoffeeByName(newOrder.getItems())
.toArray(new Coffee[] {});
return orderService.createOrder(newOrder.getCustomer(), coffeeList);
}
}
application.properties
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
info.app.author=DigitalSonic
info.app.encoding=@project.build.sourceEncoding@
server.port=8080
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.prefer-ip-address=true
resilience4j.ratelimiter.limiters.coffee.limit-for-period=5
resilience4j.ratelimiter.limiters.coffee.limit-refresh-period-in-millis=30000
#30秒内 只能接受5次coffee的请求
resilience4j.ratelimiter.limiters.coffee.timeout-in-millis=5000
#超时设置为5秒钟
resilience4j.ratelimiter.limiters.coffee.subscribe-for-events=true
#开启时间订阅
resilience4j.ratelimiter.limiters.coffee.register-health-indicator=true
#开启健康监控
resilience4j.ratelimiter.limiters.order.limit-for-period=3
resilience4j.ratelimiter.limiters.order.limit-refresh-period-in-millis=30000
resilience4j.ratelimiter.limiters.order.timeout-in-millis=1000
resilience4j.ratelimiter.limiters.order.subscribe-for-events=true
resilience4j.ratelimiter.limiters.order.register-health-indicator=true
结果分析
在 postman 中 输入 http://localhost:8080/coffee/?name=mocah 进行 查询咖啡 前几次 是好的 后面 会产生 失败
进入 http://localhost:8080/actuator/ratelimiterevents 进行查看
同样 我们使用 http://localhost:8080/order/1 去获取id为1 的订单 多次之后 返回 null
进入 http://localhost:8080/actuator/ratelimiterevents 进行查看
重启程序
使用 ab 对其进行一个并发测试
向 customer/order 做一个 5并发的post请求 post的时候 提交一个空的post的baby 总共发起20次调用
ab -c 5 -n 20 -p empty.txt http://localhost:8090/customer/order
20次 请求有17次是失败的
D:\Apache24\bin>ab -c 5 -n 20 -p empty.txt http://localhost:8090/customer/order
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient).....done
Server Software:
Server Hostname: localhost
Server Port: 8090
Document Path: /customer/order
Document Length: 185 bytes
Concurrency Level: 5
Time taken for tests: 1.080 seconds
Complete requests: 20
Failed requests: 18
(Connect: 0, Receive: 0, Length: 18, Exceptions: 0)
Non-2xx responses: 20
Total transferred: 5468 bytes
Total body sent: 2840
HTML transferred: 3088 bytes
Requests per second: 18.52 [#/sec] (mean)
Time per request: 269.999 [ms] (mean)
Time per request: 54.000 [ms] (mean, across all concurrent requests)
Transfer rate: 4.94 [Kbytes/sec] received
2.57 kb/s sent
7.51 kb/s total
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.5 1 1
Processing: 19 76 159.2 24 561
Waiting: 19 75 159.0 24 561
Total: 19 76 159.2 25 561
Percentage of the requests served within a certain time (ms)
50% 25
66% 27
75% 29
80% 30
90% 521
95% 561
98% 561
99% 561
100% 561 (longest request)
让我们看一下 http://localhost:8080/actuator/ratelimiterevents http://localhost:8090/actuator/bulkheadevents 和
有8次请求可以通过 5次请求成功
大部分 请求被拒绝
查看 http://localhost:8090/actuator/circuitbreakerevents
有8个请求传送到后端 4个成功 4个失败
RateLimiter自动配置的源代码
整体与上面的Bulkhead相似
RateLimiterConfigurationProperties
public class RateLimiterConfigurationProperties {
private int rateLimiterAspectOrder = 2147483647;
private Map<String, RateLimiterConfigurationProperties.LimiterProperties> limiters = new HashMap();
public RateLimiterConfigurationProperties() {
}
public static RateLimiterConfig createRateLimiterConfig(@Nullable RateLimiterConfigurationProperties.LimiterProperties limiterProperties) {
if (limiterProperties == null) {//默认会创建个Defaults 如果 没有 那么 就会根据给的参数 做个配置
return RateLimiterConfig.ofDefaults();
} else {
Builder rateLimiterConfigBuilder = RateLimiterConfig.custom();
if (limiterProperties.getLimitForPeriod() != null) {
rateLimiterConfigBuilder.limitForPeriod(limiterProperties.getLimitForPeriod());
}
if (limiterProperties.getLimitRefreshPeriodInMillis() != null) {
rateLimiterConfigBuilder.limitRefreshPeriod(Duration.ofMillis((long)limiterProperties.getLimitRefreshPeriodInMillis()));
}
if (limiterProperties.getTimeoutInMillis() != null) {
rateLimiterConfigBuilder.timeoutDuration(Duration.ofMillis((long)limiterProperties.getTimeoutInMillis()));
}
return rateLimiterConfigBuilder.build();
}
}
private RateLimiterConfigurationProperties.LimiterProperties getLimiterProperties(String limiter) {
return (RateLimiterConfigurationProperties.LimiterProperties)this.limiters.get(limiter);
}
public RateLimiterConfig createRateLimiterConfig(String limiter) {
return createRateLimiterConfig(this.getLimiterProperties(limiter));
}
public int getRateLimiterAspectOrder() {
return this.rateLimiterAspectOrder;
}
public void setRateLimiterAspectOrder(int rateLimiterAspectOrder) {
this.rateLimiterAspectOrder = rateLimiterAspectOrder;
}
public Map<String, RateLimiterConfigurationProperties.LimiterProperties> getLimiters() {
return this.limiters;
}
public static class LimiterProperties {
//对应的配置项
private Integer limitForPeriod;
private Integer limitRefreshPeriodInMillis;
private Integer timeoutInMillis;
private Boolean subscribeForEvents = false;
private Boolean registerHealthIndicator = false;
private Integer eventConsumerBufferSize = 100;
public LimiterProperties() {
}
@Nullable
public Integer getLimitForPeriod() {
return this.limitForPeriod;
}
public void setLimitForPeriod(Integer limitForPeriod) {
this.limitForPeriod = limitForPeriod;
}
@Nullable
public Integer getLimitRefreshPeriodInMillis() {
return this.limitRefreshPeriodInMillis;
}
public void setLimitRefreshPeriodInMillis(Integer limitRefreshPeriodInMillis) {
this.limitRefreshPeriodInMillis = limitRefreshPeriodInMillis;
}
@Nullable
public Integer getTimeoutInMillis() {
return this.timeoutInMillis;
}
public void setTimeoutInMillis(Integer timeoutInMillis) {
this.timeoutInMillis = timeoutInMillis;
}
public Boolean getSubscribeForEvents() {
return this.subscribeForEvents;
}
public void setSubscribeForEvents(Boolean subscribeForEvents) {
this.subscribeForEvents = subscribeForEvents;
}
public Integer getEventConsumerBufferSize() {
return this.eventConsumerBufferSize;
}
public void setEventConsumerBufferSize(Integer eventConsumerBufferSize) {
this.eventConsumerBufferSize = eventConsumerBufferSize;
}
public Boolean getRegisterHealthIndicator() {
return this.registerHealthIndicator;
}
public void setRegisterHealthIndicator(Boolean registerHealthIndicator) {
this.registerHealthIndicator = registerHealthIndicator;
}
}
}