避免大规模故障的微服务架构设计之道

限流是指在一段时间内,定义某个客户或应用可以接收或处理多少个请求的技术。例如,通过限流,你可以过滤掉产生流量峰值的客户和微服务,或者可以确保你的应用程序在自动扩展(Auto Scaling)失效前都不会出现过载的情况。

你还可以阻止较低优先级的流量,以便为关键事务提供足够的资源。 

微服务架构2954

限流器可以阻止流量峰值

另外有一种限流器,称为 “并发请求限流器(concurrent request limiter)”。当你有一些比较昂贵和重要的端点(endpoint),希望它不应该被调用超过指定的次数,但仍然想要提供流量服务时,这个限流器就十分有用了。

使用负载开关可以确保对于关键的事务总能提供足够的资源保障。它为高优先级的请求保留一些资源,并且不允许低优先级的事务去占用这些资源。负载开关会根据系统的整体状态做出决定,而不是基于单个用户的请求桶(request bucket)大小。负载设备有助于你的系统恢复,因为它们在持续发生故障事件时,依然能保持核心功能正常工作。

关于更多限流器和负载开关的知识,建议读者参考Stripe的相关文章。

快速且单独失效(Fail Fast and Independently)

在微服务体系架构中,我们希望服务可以快速、单独地失效。为了在服务层面隔离故障,我们可以使用隔板模式(bulkhead pattern)。可以在本文稍后看到相关介绍。

我们也希望我们的组件能够快速失效(fail fast),因为我们不希望等到断开的实例直到超时。没有什么比挂起的请求和无响应的界面更令人失望。这不仅浪费资源,而且还会让用户体验变得更差。我们的服务是互相调用的,所以在这些延迟叠加前,应该特别注意防止那些超时的操作。

你想到的第一个办法,可能是对每个服务的调用都定义超时的级别。这种做法的问题是,你不能真正知道到底什么是恰当的超时值,因为当网络故障和其他问题发生时,某些情况下只会影响一两次操作。在这种情况下,如果只有其中一些发生超时,你可能不想拒绝所有这些请求。

我们可以说,通过使用超时(timeout)来实现微服务中的快速失败是一种反模式,这是应该避免的。可以使用基于操作的成功/失败统计次数的熔断模式,而不是使用超时。

舱壁模式(Bulkheads)

在工业领域中,常使用舱壁将划分为几个部分,以便在有某部分船体发生破裂时,其他部分依然能密封安然无恙。

舱壁的概念也可以在软件开发中用于隔离资源。

通过使用舱壁模式,我们可以保护有限的资源不被用尽。例如,如果我们有两种类型的操作的话,它们都是和同一个数据库实例进行通信,并且数据据库限制连接数,这时我们可以使用两个连接池而不是使用一个共享的连接池。由于这种客户端和资源分离,超时或过度使用池的操作不会令所有其他操作失效。

泰坦尼克号沉没的主要原因之一是其舱壁设计失败,水可以通过上面的甲板倒在舱壁的顶部,最后整个船淹没。

微服务架构4029

泰坦尼克号故障的舱壁

断路器(Circuit Breakers)

为了限制操作的持续时间,我们可以使用超时。超时可以防止挂起操作并保证系统可以响应。然而,在微服务架构通信中使用静态、微调的超时是一种反模式,因为我们处于高度动态的环境中,几乎不可能确定在每种情况下都能正常工作的准确的时间限制。

我们可以使用断路器来处理错误,而不是使用小型和特定基于事务的静态超时机制。断路器以现实世界的电子元件命名,因为它们的行为是都是相同的。你可以保护资源,并通过使用断路器协助它们进行恢。断路器在分布式系统中非常有用,因为重复的故障可能会导致雪球效应,并使整个系统崩溃。