Skip to content

一、分布式系统遇到的问题

1. 服务的可用性问题

  • 服务雪崩效应: 因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程,最终导致整个系统瘫痪。
  • 在一个高度服务化的系统中,我们实现的一个业务逻辑通常会依赖多个服务
  • 如果其中的下单服务不可用,就会出现线程池里所有线程都因等待响应而被阻塞,从而造成整个服务链路不可用,进而导致整个系统的服务雪崩

二、 服务不可用的原因

1. 流量激增

  • 激增流量导致CPU飙高,无法正常处理请求。
  • 激增流量打垮冷系统(数据库连接未创建,缓存未预热)。
  • 消息投递速度过快,导致消息处理积压。

例如: 突然有成千上万客户来访问或黑客故意攻击,导致CPU飙升。

2. 被其他不稳定服务拖垮

  • 慢SQL。
  • 第三方服务响应慢。
  • 业务调用链很长导致响应超时。

例如: A服务调用B服务,B服务被其他用户大量访问或黑客故意攻击该业务功能,导致B服务延迟或拖垮服务崩溃,从而使A服务调用B服务部分功能无法使用,进一步发展服务雪崩效应。

3. 异常没处理

在服务提供者不可用的时候,会出现大量重试的情况:

  • 用户重试
  • 代码逻辑重试
  • 大量请求线程同步等待造成的资源耗尽

这些重试最终导致:进一步加大请求流量。 当服务调用者使用同步调用时,会产生大量的等待线程占用系统资源。一旦线程资源被耗尽服务调用者提供的服务也将处于不可用状态,于是服务雪崩效应产生了。

三、解决方式: 稳定性(Reliabilitly)&& 可靠性(Resilience)

1. 超时机制

在不做任何处理的情况下,服务提供者不可用会导致消费者请求线程强制等待,而造成系统资源耗尽。 加入超时机制,一旦超时,就释放资源。由于释放资源速度较快,一定程度上可以抑制资源耗尽的问题。

2. 服务限流

QPS: 服务限制用户访问请求限制次数,可以根据时间来进行控制。

例如:设置最大访问量为每秒500次数,但是当前访问量每秒800次数,300个访问量被拒绝。

  • 线程数隔离:

用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,则会进行降级处理,用户的请求不会被阻塞,至少可以看到一个执行结果(例如返回友好的提示信息),而不是无休止的等待或者看到系统崩溃。

  • 信号量隔离

信号隔离也可以用于限制并发访问防止阻塞扩散,与线程隔离最大不同在于执行依赖代码的线程依然是请求线程(该线程需要通过 信号申请),如果客户端是可信的且可以快速返回,可以使用信号隔离替换线程隔离降低开销信号量的大小可以动态调整,线程池大小不可 以

3. 服务熔断

  • 服务熔断: 远程服务不稳定或网络抖动时暂时关闭。

例如: 现实世界的断路器大家肯定都很了解,断路器实时监控电路的情况,如果发现电路电流异常,就会跳闸,从而防止电路 被烧毁。

  • 软件世界的断路器可以这样理解:实时监测应用,如果发现在一定时间内失败次数/失败率达到一定阈值,就“跳闸”,断路 器打开——此时,请求直接返回,而不去调用原本调用的逻辑。跳闸一段时间后(例如10秒),断路器会进入半开状 态,这是一个瞬间态,此时允许一次请求调用该调的逻辑,如果成功,则断路器关闭,应用正常调用;如果调用依然不 成功,断路器继续回到打开状态,过段时间再进入半开状态尝试——通过”跳闸“,应用可以保护自己,而且避免浪费资 源;而通过半开的设计,可实现应用的“自我修复“。
  • 所以,同样的道理,当依赖的服务有大量超时时,在让新的请求去访问根本没有意义,只会无畏的消耗现有资源。

例如: 我们设置了超时时间为1s,如果短时间内有大量请求在1s内都得不到响应,就意味着这个服务出现了异常,此时就没有必要再让其他的请求去访问这个依赖了,这个时候就应该使用断路器避免资源浪费。

4. 服务降级

  • 有服务熔断,必然要有服务降级。
  • 服务降级: 就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调, 返回一个缺省值。

例如:(备用接口/缓存/mock数据)。这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。