并发调用隔离之BulkHead模式学习

本文介绍了BulkHead模式的概念及其在Java中的实现方式。该模式通过隔离不稳定因素,限制其影响范围,从而提高系统的整体稳定性。文章详细阐述了使用信号量和线程池两种实现方法,并对比了它们的特点。

1 BulkHead模式是什么

bulkhead指船的防水壁,如下图:当一个防水壁漏水时不会导致整个船沉掉,起到保护作用,提高稳定性。即通过隔离的形式,让不稳定因素限制在某一个小范围内,不会导致整个系统崩溃。

在这里插入图片描述
在我们日常开发系统,对外提供服务均以HTTP或RPC的形式对外提供。如下图,每种形式在接收到请求后根据业务的不同会在内部调用多个外部依赖来完成业务。

在这里插入图片描述

一个接口会依赖多个外部远程服务。外部服务由于各种原因,可能不可靠。会出现超时,无响应的情况。这样就会导致调用线程阻塞。如果有大量的这种请求产生,则会使大量调用线程阻塞,导致对外提供的HTTP或RPC服务不可用。

因此通过隔离的形式,限制不同并发调用对资源的使用范围,提高系统的可用性。

如下图[1],hystrix中对每个远程请求分配一个线程池,并限制线程池中线程数量,最终减少故障带来的影响范围。

在这里插入图片描述

2 Java中实现BulkHead的两种方式

通过第一节的介绍显而易见,可以通过线程隔离的形式来控制,即限制调用并发数

在Java中可以两种主要控制方式如下:

  • Semaphore(信号量)
  • 每个远程调用分配一个线程池的形式来实现。

日常服务我们对外提供的服务以Http或RPC为主。这两种主要是通过每请求每线程的形式对外提供服务。最终我们在请求线程中再次调用外部依赖完成调用。

我们隔离的目的就是线程特定请求并发调用数量,这样保护HTTP或RPC处理请求的线程池不被打满,保证服务可用

最终将所有依赖进行包装。并将系统中对外部的调用路由到一个个包装模块上,通过信号量大小或线程池大小实现限制。

图2

2.1 信号量并发数限制

通过第二节的介绍,当请求进入系统,服务依赖外部调用。我们将外部调用封装,并创建全局实例。对全系统中这个请求的调用通过信号量进行限制。这样的方式,RPC或HTTP服务线程池不会因某个依赖被打满。

这种方式的确定显而易见,通过信号量限制的方式,调用线程会阻塞在外部调用上,没法并发处理多个无顺序依赖关系的外部调用。此外没法像线程池获取异步调用结果一样处理超时,及时释放线程。因此需要RPC超时设置且可靠,使用时,外部依赖需要保证能够快速失败或具备一定可靠性。

这种模式下设置信号量大小时,根据调用服务线程数量及依赖的业务的情况进行划分。

2.2 线程池限制并发数

依然对外部依赖进行包装,每个依赖创建一个线程池,并固定线程数量。那么这种模式下的并发限制就是通过线程池大小来限制。当对外部依赖调用打满所关联的线程池,那么就被限制住。

通过这种方式,调用线程不会阻塞在某个外部调用上。如果一个请求依赖多个无依赖关系的外部调用,这时也可以并发调用。最终等待这些线程返回的结果,同时可以通过设置超时时间来避免让一个调用程阻塞过久,提前返回。

关于线程池隔离的其它优缺点可以参考netflix[2]的介绍。

3 隔离的开源工具

常见的如hystrix及resilience4j-bulkhead提供隔离的能力,限制并发调用数。

4 参考资料

[1]Isolation,https://github.com/Netflix/Hystrix/wiki/How-it-Works#Isolation
[2]threads,https://github.com/Netflix/Hystrix/wiki/How-it-Works#Threads

### 舱壁模式的原理 舱壁模式Bulkhead Pattern)是一种常见的微服务架构设计模式,用于隔离不同服务调用之间的资源,防止一个服务的故障影响整个系统的稳定性。其核心原理是通过为每个服务调用分配独立的线程池或资源池,确保即使某个服务出现故障,也只会消耗其专属资源,而不会影响其他服务的正常运行。这种模式类似于船舶设计中的舱壁结构,将船体分割为多个独立的水密舱室,以防止船体某处破损时导致整艘船沉没[^2]。 在网关限流场景中,舱壁模式通常用于隔离不同的客户端请求或服务依赖。例如,Spring Cloud Gateway 或其他网关组件可以通过为每个服务分配独立的线程池来实现资源隔离,从而避免因某个服务的高并发或故障导致网关整体性能下降或崩溃。 ### 网关限流中的舱壁模式配置方法 在实际应用中,可以使用 Spring Cloud Gateway 结合 Resilience4j 或 Sentinel 等工具来实现舱壁模式。 #### 1. 使用 Resilience4j 配置舱壁模式 Resilience4j 提供了基于线程池隔离和信号量隔离的机制。以下是使用 Resilience4j 的线程池隔离配置示例: ```yaml resilience4j.thread-pool-bulkhead: configs: default: maxThreadPoolSize: 10 coreThreadPoolSize: 5 queueSize: 20 ``` 在上述配置中,`maxThreadPoolSize` 表示最大线程池大小,`coreThreadPoolSize` 表示核心线程池大小,`queueSize` 表示任务队列大小。通过这些配置,可以为每个服务调用分配独立的线程池资源。 #### 2. 使用 Sentinel 配置舱壁模式 Sentinel 支持信号量隔离策略,可以通过配置信号量的大小来限制并发请求的数量。以下是 Sentinel 的信号量隔离配置示例: ```java // 配置信号量隔离规则 List<Rule> rules = new ArrayList<>(); SemaphoreRule rule = new SemaphoreRule(); rule.setResource("yourResourceName"); rule.setLimitApp("default"); rule.setMaxConcurrent(10); // 设置最大并发数 rules.add(rule); // 加载规则 RuleManager.loadRules(rules); ``` 在上述代码中,`setResource` 方法用于指定资源名称,`setMaxConcurrent` 方法用于设置最大并发数。通过这种方式,可以限制每个资源的并发请求量,从而实现舱壁模式的资源隔离。 ### 舱壁模式的应用场景 舱壁模式适用于以下几种场景: - **多租户系统**:在多租户系统中,不同租户的请求需要被隔离,以防止某个租户的高并发请求影响其他租户的服务质量。 - **微服务架构**:在微服务架构中,服务之间的调用需要被隔离,以防止某个服务的故障影响整个系统的稳定性。 - **高并发系统**:在高并发系统中,通过为不同的请求分配独立的线程池或资源池,可以有效控制资源的使用情况,避免系统过载。 ### 舱壁模式的优缺点 **优点**: - **资源隔离**:通过为每个服务调用分配独立的线程池或资源池,确保即使某个服务出现故障,也不会影响其他服务的正常运行。 - **稳定性提升**:舱壁模式能够有效防止一个服务的故障扩散到整个系统,从而提升系统的整体稳定性。 **缺点**: - **资源开销**:为每个服务调用分配独立的线程池会增加系统的资源开销,尤其是在服务数量较多的情况下。 - **复杂性增加**:舱壁模式的实现和维护相对复杂,需要额外的配置和管理。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值