一、Resilience4j 实现断路器
创建SpringBoot 项目 resilience4j-circuitbreaker-demo
1.1 依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>0.14.1</version>
</dependency>
1.2 配置
resilience4j:
circuitbreaker: #断路器
backends:
menu: #menu 熔断器
event-consumer-buffer-size: 10
failure-rate-threshold: 50
ring-buffer-size-in-closed-state: 5
ring-buffer-size-in-half-open-state: 3
wait-duration-in-open-state: 5000
order: #order 熔断器
event-consumer-buffer-size: 10
failure-rate-threshold: 50
ring-buffer-size-in-closed-state: 5
ring-buffer-size-in-half-open-state: 3
wait-duration-in-open-state: 5000
1.3 注册和处理熔断
@RestController
@RequestMapping("/customer")
@Slf4j
public class CustomerController {
@Autowired
private CoffeeService coffeeService;
@Autowired
private CoffeeOrderService coffeeOrderService;
private CircuitBreaker circuitBreaker;
/**
* 代码方式
* 注册 "menu" 断路器
* 注册 "menu" 限流器
*/
public CustomerController(CircuitBreakerRegistry circuitBreakerRegistry) {
circuitBreaker = circuitBreakerRegistry.circuitBreaker("menu");
}
@GetMapping("/getCoffeeMenu")
public List<String> getCoffeeMenu() {
return Try.ofSupplier(
CircuitBreaker.decorateSupplier(circuitBreaker,
() -> coffeeService.coffee()))
.recover(CircuitBreakerOpenException.class, Collections.emptyList()) //熔断处理
.get(); //返回结果
}
/**
* @return
* @io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker 注解方式断路器 order
*/
@PostMapping("order")
@io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker(name = "order")
public Map<String, Object> createOrder() {
Map<String, Object> stringObjectMap = new HashMap<>();
stringObjectMap.put("name", "布卡");
log.info("Order {}", stringObjectMap);
return coffeeOrderService.coffeeOrder(stringObjectMap);
}
}
1.4 测试
- docker 启动 consul 服务注册中心
- 启动项目 consul-waiter-service
- 启动项目 resilience4j-circuitbreaker-demo
```shell
执行
curl -X GET http://localhost:8011/customer/getCoffeeMenu返回
[“38029 “,”ESPRESSO COFFEES $12”,”HANDMADE COFFEES $15”,”SOOTHING HOT ALTERNATIVES $20”,”COLD ALTERNATIVES $8”]
关闭consul-waiter-service项目
快速执行多次
curl -X GET http://localhost:8011/customer/getCoffeeMenu
返回[],熔断生效
<a name="KGaAi"></a>
## 二、Resilience4j 实现限流
<a name="bulkhead"></a>
### 2.1 bulkhead
复制项目resilience4j-circuitbreaker-demo 修改为 bulkhead-customer-service
<a name="09cd0eaf"></a>
#### 2.1.1 配置
```yaml
resilience4j:
circuitbreaker: #断路器
backends:
menu: #menu 熔断器
event-consumer-buffer-size: 10
failure-rate-threshold: 50
ring-buffer-size-in-closed-state: 5
ring-buffer-size-in-half-open-state: 3
wait-duration-in-open-state: 5000
order: #order 熔断器
event-consumer-buffer-size: 10
failure-rate-threshold: 50
ring-buffer-size-in-closed-state: 5
ring-buffer-size-in-half-open-state: 3
wait-duration-in-open-state: 5000
#限流控制
bulkhead:
backends:
menu:
max-concurrent-call: 5 #最多5个线程
max-wait-time: 5 #最多等待5s
order:
max-concurrent-call: 1
max-wait-time: 5
2.1.2 注册和限制
@RestController
@RequestMapping("/customer")
@Slf4j
public class CustomerController {
@Autowired
private CoffeeService coffeeService;
@Autowired
private CoffeeOrderService coffeeOrderService;
private CircuitBreaker circuitBreaker;
private Bulkhead bulkhead;
/**
* 代码方式
* 注册 "menu" 断路器
* 注册 "menu" 限流器
*/
public CustomerController(CircuitBreakerRegistry circuitBreakerRegistry,
BulkheadRegistry bulkheadRegistry) {
circuitBreaker = circuitBreakerRegistry.circuitBreaker("menu");
bulkhead = bulkheadRegistry.bulkhead("menu");
}
@GetMapping("/getCoffeeMenu")
public List<String> getCoffeeMenu() {
return Try.ofSupplier(
Bulkhead.decorateSupplier(bulkhead,
CircuitBreaker.decorateSupplier(circuitBreaker,
() -> coffeeService.coffee())))
.recover(CircuitBreakerOpenException.class, Collections.emptyList()) //熔断处理
.recover(BulkheadFullException.class, Collections.emptyList())
.get(); //返回结果
}
/**
* @return
* @io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker 注解方式断路器 order
* @io.github.resilience4j.bulkhead.annotation.Bulkhead 注解方式限流 order
*/
@PostMapping("order")
@io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker(name = "order")
@io.github.resilience4j.bulkhead.annotation.Bulkhead(name = "order")
public Map<String, Object> createOrder() {
Map<String, Object> stringObjectMap = new HashMap<>();
stringObjectMap.put("name", "布卡");
log.info("Order {}", stringObjectMap);
return coffeeOrderService.coffeeOrder(stringObjectMap);
}
}
2.1.3 测试
- docker 启动 consul 服务注册中心
- 启动项目 consul-waiter-service
- 启动项目 bulkhead-customer-service
- 安装 ab 性能测试工具 ```shell sudo apt-get update sudo apt-get install apache2-utils
测试
-n 总的请求数 -c 一次同时并发的请求数 ab -n 10 -c 5 http://localhost:8011/customer/getCoffeeMenu
- 输出结果
![image.png](https://cdn.nlark.com/yuque/0/2020/png/438760/1596377597208-28351c26-bfdf-4091-b832-1cf6415639a4.png#align=left&display=inline&height=459&margin=%5Bobject%20Object%5D&name=image.png&originHeight=918&originWidth=1048&size=181003&status=done&style=none&width=524)
- 加大并发(结果有13个失败)
```shell
ab -n 20 -c 10 http://localhost:8011/customer/getCoffeeMenu
2.2 RateLimiter
创建项目 ratelimiter-waiter-service
2.2.1 配置
resilience4j:
ratelimiter:
limiters:
coffee:
limit-for-period: 5 #限制5次
limit-refresh-period-in-millis: 30000 #30秒
register-health-indicator: true
subscribe-for-events: true
timeout-in-millis: 5000 #5秒超时
order:
limit-for-period: 3
limit-refresh-period-in-millis: 30000
register-health-indicator: true
subscribe-for-events: true
timeout-in-millis: 1000
2.2.2 注册RateLimiter
@Slf4j
@RestController
public class WaiterController {
private RateLimiter rateLimiter;
public WaiterController(RateLimiterRegistry rateLimiterRegistry) {
//非注解方式,注册限流器
rateLimiter = rateLimiterRegistry.rateLimiter("coffee");
}
/**
* @param request
* @return
*/
@GetMapping(value = "/coffee", produces = "application/json")
public List<String> coffeeMenu(HttpServletRequest request) {
List<String> result = null;
try {
result = rateLimiter.executeSupplier(() -> Arrays.asList(request.getServerPort() + " "
, "ESPRESSO COFFEES $12"
, "HANDMADE COFFEES $15"
, "SOOTHING HOT ALTERNATIVES $20"
, "COLD ALTERNATIVES $8")
);
log.info("Get Order: {}", result);
} catch (RequestNotPermitted e) {
log.warn("Request Not Permitted! {}", e.getMessage(), e);
}
return result;
}
/**
* 注解方式限流
*
* @param params
* @return
*/
@io.github.resilience4j.ratelimiter.annotation.RateLimiter(name = "order")
@PostMapping(value = "/order", consumes = "application/json")
public Map<String, Object> coffeeMenu(@RequestBody Map<String, Object> params) {
log.info("order coffee");
params.put("status", "success");
params.put("payMoney", "$19");
return params;
}
}
2.2.3 测试
- 本地docker 启动consul 服务注册中心
- 启动项目 bulkhead-customer-service
- 启动项目 ratelimiter-waiter-service
访问 http://localhost:8011/customer/getCoffeeMenu ,接口限制30秒内访问5次
项目地址
https://github.com/h-dj/SpringCloud-Learning