d断路器模式(接受失败是为了更好的成功)

问题与背景

处理连接到远程服务或资源时可能需要不同时间才能纠正的故障。存在故障是由于不易预料的意外事件造成的,并且可能需要更长的时间来纠正。这些故障的严重程度可以从部分连接丢失到服务完全失败。在这些情况下,应用程序不断重试执行不太可能成功的操作可能毫无意义,相反,应用程序应迅速接受该操作已失败并相应地处理此失败。

解决方案

1、断路器模式要防止应用程序重复尝试执行可能失败的操作,允许它继续运行,无需等待

2、断路器模式还使应用程序能够检测故障是否已解决。

代理

代理应该监视最近发生的失败次数,然后使用此信息来决定是允许操作继续进行,还是立即返回异常。

代理可以实现为具有以下模仿电气断路器功能的状态的状态机:

Closed:如果对操作的调用不成功,代理会增加此计数。如果在给定时间段内最近失败的数量超过了指定的阈值,则代理将进入打开状态。此时代理启动一个超时计时器,当该计时器到期时,代理将进入半开状态。

Open:来自应用程序的请求立即失败,并向应用程序返回异常。

半开放:允许来自应用程序的有限数量的请求通过并调用操作。如果这些请求成功,则假定先前导致故障的故障已修复,并且断路器切换到Closed状态(故障计数器被重置)。如果任何请求失败,断路器会假定故障仍然存在,因此它会恢复到打开状态并重新启动超时计时器,以使系统有更多时间从故障中恢复。

超时计时器的目的是在允许应用程序再次尝试执行操作之前,给系统时间来纠正导致失败的问题。

半开状态有助于防止正在恢复的服务突然被请求淹没。当服务恢复时,它可能能够支持有限数量的请求,直到恢复完成,但在恢复过程中,大量工作可能会导致服务超时或再次失败。

该模式是可定制的,并且可以根据可能的故障的性质进行调整。例如,您可以将增加的超时计时器应用于断路器。您最初可以将断路器置于打开状态几秒钟,然后如果故障尚未解决,则将超时时间增加到几分钟,依此类推。在某些情况下,与其让Open状态返回失败并引发异常,不如返回对应用程序有意义的默认值。

问题和注意事项

何时使用此模式

使用此模式:

  • 如果此操作很可能失败,则防止应用程序尝试调用远程服务或访问共享资源。

此模式可能不适合:

  • 用于处理对应用程序中本地私有资源的访问,例如内存数据结构。在这种环境中,使用断路器只会增加系统的开销。
  • 代替处理应用程序业务逻辑中的异常。

相关模式和指导

在实现此模式时,以下模式也可能是相关的:

  • 重试模式。重试模式是断路器模式的有用补充。它描述了应用程序在尝试连接到服务或网络资源时如何处理预期的临时故障,方法是透明地重试先前失败的操作,并期望故障原因是暂时的。
  • 健康端点监控模式。断路器可能能够通过向服务公开的端点发送请求来测试服务的健康状况。该服务应返回指示其状态的信息。

kitex中采取的策略

Kitex 默认提供了三个基本的熔断触发策略:

  • 连续错误数达到阈值 (ConsecutiveTripFunc)
  • 错误数达到阈值 (ThresholdTripFunc)
  • 错误率达到阈值 (RateTripFunc)

熔断器会统计一段时间窗口内的成功,失败和超时,默认窗口大小是 10S;

重试模式(不达目的不罢休)

解决瞬态故障

问题与背景

应用程序必须对该环境中可能发生的瞬态故障敏感。此类故障包括与组件和服务的网络连接暂时丢失、服务暂时不可用或服务繁忙时出现的超时。这些故障通常是自我纠正的,如果触发故障的动作在适当的延迟后重复,它很可能是成功的。例如,正在处理大量并发请求的数据库服务可能会实施一种限制策略,在其工作负载减轻之前暂时拒绝任何进一步的请求。尝试访问数据库的应用程序可能无法连接,但如果它在适当的延迟后再次尝试,它可能会成功。

解决方案

如果应用程序在尝试向远程服务发送请求时检测到失败,它可以使用以下策略来处理失败:

  • 如果故障表明失败不是暂时的,或者如果重复失败则不太可能成功(例如,由于提供无效凭据而导致的身份验证失败,无论尝试多少次都不太可能成功),则应用程序应中止操作并报告一个合适的例外。(不再重试)
  • 如果报告的特定故障异常或罕见,则可能是由异常情况引起的,例如网络数据包在传输过程中损坏。在这种情况下,应用程序可以立即重试失败的请求,因为同样的失败不太可能重复,并且请求可能会成功。(立即重试)
  • 如果故障是由更常见的连接或“繁忙”故障之一引起的,则网络或服务可能需要很短的时间来纠正连接问题或清除积压的工作。在重试请求之前,应用程序应该等待一段合适的时间。(随机重试,诸如指数回退之类的计时策略)

问题和注意事项

由实现重试策略的应用程序调用的服务中的操作可能需要是幂等的。

考虑重试作为事务的一部分的操作将如何影响整个事务的一致性。

记录所有提示重试的连接失败非常重要,这样可以识别应用程序、服务或资源的潜在问题。

何时使用此模式

使用此模式:

  • 当应用程序在与远程服务交互或访问远程资源时可能会遇到瞬时故障。这些故障预计是短暂的,重复先前失败的请求可能会在后续尝试中成功。

此模式可能不适合:

  • 当故障可能持续很长时间时,因为这会影响应用程序的响应能力。应用程序可能只是在浪费时间和资源尝试重复最有可能失败的请求。
  • 用于处理非暂时性故障引起的故障,例如应用程序的业务逻辑错误导致的内部异常。
  • 作为解决系统中可扩展性问题的替代方案。如果应用程序经常遇到“繁忙”故障,这通常表明正在访问的服务或资源应该扩大规模。

kitex中采取的策略

超时重试、Backup Request,建连失败重试(默认)。其中建连失败是网络层面问题,由于请求未发出,框架会默认重试。

  • 超时重试:提高服务整体的成功率
  • Backup Request:减少服务的延迟波动

因为很多的业务请求不具有幂等性,这两类重试不会作为默认策略。

超时重试

MaxRetryTimes:最大重试次数

MaxDurationMS:累计最大耗时,包括首次失败请求和重试请求耗时,如果耗时达到了限制的时间则停止后续的重试

EERThreshold:重试熔断错误率阈值, 方法级别请求错误率超过阈值则停止重试。

ChainStop:链路中止, 默认启用。如果上游请求是重试请求,超时后不会重试。

DDLStop:链路超时中止,该策略是从链路的超时时间判断是否需要重试。

BackOff:重试等待策略,默认立即重试(NoneBackOff)。可选:固定时长退避 (FixedBackOff)、随机时长退避 (RandomBackOff)。

RetrySameNode:框架默认选择其他节点重试,若需要同节点重试,可配置为 true。

Backup Request

RetryDelayMS:Backup Request 的等待时间,若该时间内若请求未返回,会发送新的请求。必须手动配置

健康端点健康模式

背景与问题

监控 Web 应用程序、中间层和共享服务是一种很好的做法(通常是业务需求),以确保它们可用并正确执行。

解决方案

通过向应用程序上的端点发送请求来实施健康监控。应用程序应执行必要的检查,并返回其状态指示。

健康监控检查通常结合两个因素:应用程序或服务为响应对健康验证端点的请求而执行的检查(如果有),以及执行健康验证检查的工具或框架对结果的分析。响应代码指示应用程序的状态,以及它使用的任何组件或服务(可选)。延迟或响应时间检查由监控工具或框架执行。图 1 显示了该模式的实现概览。

问题和注意事项

何时使用此模式

  • 监控网站和 Web 应用程序以验证可用性。
  • 监控网站和 Web 应用程序以检查是否正确运行。
  • 监控中间层或共享服务以检测和隔离可能中断其他应用程序的故障。
  • 补充应用程序中的现有工具,例如性能计数器和错误处理程序。健康验证检查不会取代应用程序中的日志记录和审计要求。Instrumentation 可以为监控计数器和错误日志以检测故障或其他问题的现有框架提供有价值的信息。但是,如果应用程序不可用,它就无法提供信息。

优先队列模式

背景与问题

对发送到服务的请求进行优先级排序,以便接收和处理具有较高优先级的请求比那些具有较低优先级的请求更快。这种模式在为各个客户端提供不同服务级别保证的应用程序中很有用。

解决方案

使用支持消息优先级的排队机制

为每个优先级使用单独的消息队列

使用优先级排队机制可以提供以下优势:

  • 它允许应用程序满足需要优先考虑可用性或性能的业务需求,例如为特定的客户群体提供不同级别的服务。
  • 它可以帮助最大限度地降低运营成本。在单队列方法中,您可以在必要时缩减消费者数量。高优先级的消息仍将首先处理(尽管可能更慢),而低优先级的消息可能会延迟更长的时间。如果你已经实现了多消息队列方法,每个队列有单独的消费者池,你可以减少低优先级队列的消费者池,甚至通过停止所有监听消息的消费者来暂停一些非常低优先级队列的处理。那些队列。
  • 多消息队列方法可以通过根据处理要求对消息进行分区来帮助最大化应用程序的性能和可伸缩性。例如,重要任务可以优先由立即运行的接收器处理,而不太重要的后台任务可以由计划在不太繁忙的时段运行的接收器处理。

问题与注意事项

  • 在解决方案的上下文中定义优先级。例如,“高优先级”可能意味着应该在十秒内处理消息。确定处理高优先级项目的要求,以及必须分配哪些其他资源才能满足这些标准。
  • 决定是否必须先处理所有高优先级项目,然后再处理任何低优先级项目。如果消息正在由单个消费者池处理,则可能需要提供一种机制,该机制可以在更高优先级的消息可用时抢占并暂停正在处理低优先级消息的任务。
  • 在多队列方法中,当使用单个消费者进程池来侦听所有队列而不是为每个队列使用专用消费者池时,消费者必须应用一种算法,以确保它始终在处理来自较低优先级队列的消息之前为来自较高优先级队列的消息提供服务排队。
  • 监控高优先级和低优先级队列的处理速度,以确保这些队列中的消息以预期的速率处理。
  • 如果您需要保证处理低优先级消息,则可能需要实现具有多个消费者池的多消息队列方法。或者,在支持消息优先级的队列中,可能会随着队列消息的老化而动态增加队列消息的优先级。但是,这种方法取决于提供此功能的消息队列。
  • 为每个消息优先级使用单独的队列最适合具有少量明确定义的优先级的系统。
  • 消息优先级可以由系统在逻辑上确定。例如,可以将它们指定为“付费客户”或“非付费客户”,而不是具有明确的高优先级和低优先级消息。根据您的业务模型,您的系统可能会分配更多资源来处理来自付费客户的消息,而不是非付费客户。
  • 检查队列中的消息可能会产生财务和处理成本(一些商业消息传递系统在每次发布或检索消息时以及每次查询队列中的消息时都会收取少量费用)。检查多个队列时,此成本将增加。

何时使用此模式

此模式非常适合以下场景:

  • 系统必须处理可能具有不同优先级的多个任务。
  • 应该以不同的优先级为不同的用户或租户提供服务。

竞争消费者模式

背景与问题

允许多个并发消费者处理在同一消息传递通道上接收到的消息。这种模式使系统能够同时处理多个消息以优化吞吐量、提高可伸缩性和可用性以及平衡工作负载。

在云中运行的应用程序可能会处理大量请求。一种常见的技术不是同步处理每个请求,而是让应用程序通过消息传递系统将它们传递给另一个异步处理它们的服务(消费者服务)。此策略有助于确保在处理请求时不会阻塞应用程序中的业务逻辑。

解决方案

它启用了一个固有的负载均衡系统

它提高了可靠性

它不需要消费者之间或生产者和消费者实例之间的复杂协调。

它是可扩展的。

如果消息队列提供事务性读取操作,它可以提高弹性。

问题与注意事项

留言订购。消费者服务实例接收消息的顺序无法保证,也不一定反映消息的创建顺序。设计系统以确保消息处理是幂等的,因为这将有助于消除对消息处理顺序的任何依赖。

设计弹性服务。如果系统设计用于检测和重新启动失败的服务实例,则可能需要将服务实例执行的处理实现为幂等操作,以最小化单个消息被多次检索和处理的影响。

检测毒药信息。格式错误的消息或需要访问不可用资源的任务可能会导致服务实例失败。系统应防止此类消息返回队列,而是捕获这些消息的详细信息并将其存储在其他位置,以便在必要时对其进行分析。

处理结果。处理消息的服务实例与生成消息的应用程序逻辑完全解耦,它们可能无法直接通信。如果服务实例生成的结果必须传回应用程序逻辑,则此信息必须存储在双方都可以访问的位置,并且系统必须提供一些处理完成时间的指示,以防止应用程序逻辑检索不完整的数据.

扩展消息系统。在大规模解决方案中,单个消息队列可能会被消息数量所淹没,并成为系统的瓶颈。在这种情况下,请考虑对消息系统进行分区以将消息从特定生产者定向到特定队列,或者使用负载平衡将消息分发到多个消息队列。

**确保消息系统的可靠性。**需要一个可靠的消息传递系统来保证,一旦应用程序将消息加入队列,它就不会丢失。这对于确保所有消息至少传递一次至关重要。

何时使用此模式

在以下情况下使用此模式:

  • 应用程序的工作负载分为可以异步运行的任务。
  • 任务是独立的,可以并行运行。
  • 工作量变化很大,需要可扩展的解决方案。
  • 该解决方案必须提供高可用性,并且在任务处理失败时必须具有弹性。

在以下情况下,此模式可能不适合:

  • 将应用程序工作负载分离成离散的任务并不容易,或者任务之间存在高度依赖关系。

  • 任务必须同步执行,应用程序逻辑必须等待任务完成才能继续。

  • 任务必须按特定顺序执行。

    一些消息传递系统支持使生产者能够将消息组合在一起并确保它们都由同一个消费者处理的会话。此机制可与优先消息(如果支持)一起使用,以实现一种消息排序形式,将消息按顺序从生产者传递到单个消费者。