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

健康检查和负载均衡

因为故障或部署、自动扩展等原因,服务实例会不停启动,重新启动及停止。这使得服务暂时或一直停用。为了避免发生这些问题,在负载均衡中应该在路由中设置忽略这些实例,因为它们无法为子系统或用户提供服务。 

我们可以通过外部观察去判断应用实例是否健康。你可以多次调用 

Get /health的端点(endpoint)或者通过自身服务的报告获得相关信息。现在的 

服务发现解决方案会持续从实例中收集健康信息,并且设置负载均衡的路由,让其只指向健康的实例组件。

自我修复

自我修复能帮助恢复应用。我们讨论下当应用遇到崩溃状态后,如何通过相关的步骤去自我修复。在大多数情况下,是通过外部系统监控实例的状态,当服务出现故障一段时间后则会重启服务。在大多数情况下,自我修复的功能是相当有用的,然而,在某些情况下由于不断地重启服务会带来相关的问题。例如当服务过载或者数据库连接超时,则会导致应用不能反馈正确的服务健康状态。

对于一些场景-比如数据库链接丢失,这个时候实现高级的自我修复功能是颇为棘手的。在这种情况下,需要为应用添加额外的逻辑去处理这些特例,并且让外部系统知道服务的实例不需要立即重新启动。

故障转移缓存(Failover Caching)

因为网络问题和系统中的变更,服务通常会出现故障。然而,这些故障中断大多是暂时的,这要归功于自我修复和高级负载平衡的功能,我们应该找到一个解决方案,能使服务即使在出现故障的时候也能工作。这就是故障转移缓存(Failover Caching),它能帮助为我们的应用提供必需的数据。

失效转移缓存通常使用两个不同的过期日期:其中更短的日期指示在正常情况下能使用缓存的时间,而更长的一个日期则指示在故障失效的时候,能使用缓存中的数据时长。

微服务架构2077

故障转移缓存

特别需要提醒的是,只有当提供过时的数据比没有数据更好的情况下,才能使用故障转移缓存。

要设置缓存和故障转移缓存,可以在HTTP中使用标准响应头。

例如,使用max-age头可以指定某个资源为新资源的最大时间(译者注:意即设定max-age后,浏览器不再发送请求到服务器)。可以使用stale-if-error 头去确定在出现故障的情况下,从缓存获取资源的时间长短。

现在的CDN和负载均衡器提供了各种缓存和故障转移的解决方案,但是你也可以在你的公司中建立一个共享库,其中包括这些标准的可靠性解决方案。

重试逻辑(Retry Logic)

在某些情况下,我们可能无法缓存数据,或者想对数据进行变更,但是操作最终失败了。在这种情况下,我们就可以选择重试操作,因为我们可以预期资源将在一段时间后恢复,或者负载均衡会将请求发送到健康的实例上。

你应该小心地为应用程序和客户端添加重试逻辑,因为更大量的重试操作可能会使事情变得更糟,甚至阻止应用程序恢复。

在分布式系统中,微服务系统重试可能会触发多个其他请求或重试操作,并导致级联效应。为减少重试带来的影响,你应该减少重试的数量,并使用指数退避算法(exponential backoff algorithm)来持续增加重试之间的延迟时间,直到达到最大限制。

由于重试是由客户端(浏览器,其他微服务等)发起的,并且客户端在处理请求前后是不知道草走失败的,你应该为你的应用程序提供幂等处理能力。例如,当你重试购买操作时,不应该向客户收两次钱。给每个事务使用唯一的幂等键(idempotency-key)是解决重试问题的方法。

限流器和负载开关(Rate Limiters and Load Shedders)