使用 Resilience4j 实现服务限流

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;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值