Apache Camel 中的短重试与长重试

位置:首页>文章>详情   分类:Java教程   阅读(114)   2023-12-05 07:14:57

Camel Design Patterns 一书描述了 20 种模式以及用于设计基于 Apache Camel 的集成解决方案的大量技巧和最佳实践。每个模式都基于真实世界的用例,并提供 Camel 特定的实现细节和最佳实践。为了感受这本书,下面是书中重试模式的摘录,描述了如何在 Apache Camel 中进行短期和长期退休。

背景和问题

就其本质而言,集成应用程序必须通过网络与其他系统进行交互。随着基于云的动态环境成为常态,微服务架构风格将应用程序划分为更细粒度的服务,成功的服务通信已成为许多分布式应用程序的基本先决条件。与其他服务通信的服务必须能够透明地处理下游系统中可能发生的瞬时故障,并在没有任何中断的情况下继续运行。由于瞬时故障可被视为基础设施级故障、网络连接丢失、超时和繁忙服务应用的限制等。这些情况很少发生,它们通常会自我纠正,并且通常重试操作会成功。

力与解

重现和解释瞬时故障可能是一项艰巨的任务,因为这些可能是由不规则发生且与外部系统相关的多种因素组合引起的。 Chaos Monkey 等工具可用于模拟不可预测的系统中断,并让您在需要时测试应用程序的弹性。处理暂时性故障的一个好策略是重试操作并希望它会成功(如果错误确实是暂时性的,它会成功;保持冷静并继续重试)。

要实现“重试”逻辑,需要考虑以下几个方面:

重试哪些失败?

某些服务操作(例如 HTTP 调用和关系数据库交互)是重试逻辑的潜在候选者,但在实施之前需要进一步分析。关系数据库可能会拒绝连接尝试,因为它正在限制过度的资源使用,或者因为并发修改而拒绝 SQL 插入操作。在这些情况下重试可能会成功。但是,如果关系数据库因为凭据错误而拒绝连接,或者 SQL 插入操作因为外键约束而失败,则重试该操作将无济于事。与 HTTP 调用类似,重试连接超时或响应超时可能会有所帮助,但重试由业务错误引起的 SOAP Fault 没有任何意义。因此请谨慎选择重试次数。

多久重试一次?

一旦确定了重试的必要性,就应该调整特定的重试策略以满足两个应用程序的性质:具有重试逻辑的服务消费者和具有暂时性故障的服务提供者。例如,如果实时集成服务无法处理请求,则可能只允许它在返回响应之前进行几次重试,延迟很短,而基于批处理的异步服务可能能够进行更多重试更长的延迟和指数回退。重试策略还应考虑其他因素,例如服务消费合同和服务提供商的 SLA。例如,一个非常激进的重试策略可能会导致进一步的节流,甚至将服务消费者列入黑名单,或者它可能会完全超载并降级繁忙的服务,并完全阻止它恢复。某些 API 可能会指示您一段时间内的剩余请求计数以及响应中的黑名单信息,但有些 API 可能不会。因此,重试策略定义了重试的频率以及重试的时间,然后您应该接受这是非暂时性故障并放弃的事实。

幂等性

重试操作时,请考虑对该操作可能产生的副作用。将与重试逻辑一起使用的服务操作应设计和实现为幂等的。使用相同的数据输入重试相同的操作不应该有任何副作用。想象一个已成功处理的请求,但响应尚未返回。服务消费者可能会假设请求失败并重试相同的操作,这可能会产生一些意想不到的副作用。

监控

跟踪和报告重试也很重要。如果某些操作在成功之前不断重试,或者在失败之前重试太多次,则必须识别并修复这些操作。由于服务中的重试对服务消费者来说应该是透明的,如果没有适当的监控,它们可能不会被发现并以负面方式影响整个系统的稳定性和性能。

超时和 SLA

当下游系统发生瞬态故障,重试逻辑启动时,重试服务的整体处理时间将显着增加。重要的是从服务 SLA 和服务消费者超时的角度来驱动这些值,而不是从重试次数和延迟的角度来考虑重试参数。因此,采用允许处理请求的最大时间量,并确定可以压缩到该时间范围内的最大重试和延迟次数(包括处理时间)。

力学

有几种不同的方法可以使用 Camel 和 ActiveMQ 执行重试。

Camel RedeliveryPolicy(短重试)

这是在 Camel 中进行重试的最流行和通用的方法。重新传递策略定义了重试规则(例如重试和延迟的次数、是否使用冲突避免和指数退避乘数以及日志记录),然后可以将其应用于处理流程的多个 errorHandler 和 onException 块。每当抛出异常时,将应用重新传递策略中的规则。

Camel RedeliveryPolicy 示例

重试机制的关键区别在于,Camel 错误处理逻辑不会重试整个路由,而只会重试处理流程中失败的端点。这要归功于连接 Camel 路线中端点的通道。每当处理节点抛出异常时,它就会传播回去并被通道捕获,然后通道可以应用各种错误处理策略。这里的另一个重要区别是,基于 Camel 的错误处理和重新交付逻辑是在内存中的,它会在重试期间阻塞线程,这会产生后果。如果所有线程都被阻塞并等待重试,您可能会用完线程。线程的所有者可能是消费者,或者是一些具有路由线程池的并行处理构造(例如并行拆分器、接收者列表或线程 DSL)。例如,如果我们有一个具有 10 个请求处理线程的 HTTP 消费者,一个繁忙并拒绝连接的数据库,以及一个具有指数退避的 RedeliveryPolicy,那么在 10 个请求之后所有线程将最终等待重试并且没有线程会可用于处理新请求。这种线程阻塞问题的解决方案是选择 asyncDelayedRedelivery,其中 Camel 将使用线程池并异步安排重新交付。但是线程池将重新传送请求存储在内部队列中,因此此选项可以非常快地消耗所有堆。还要记住,CamelContext 的所有错误处理程序和重新交付都有一个线程池,因此除非您配置特定的线程池以实现持久的重新交付,否则该池可能会在一个路由中耗尽并在另一个路由中阻塞线程。另一个含义是,由于重试逻辑在内存中的性质,重新启动应用程序将丢失重试状态,并且无法分发或保留此状态。

总的来说,这种 Camel 重试机制适用于短暂的本地重试,并可以克服网络故障或资源的短暂锁定。对于更长时间的延迟,重新设计具有集群和非线程阻塞的持久重新交付的应用程序是更好的选择(这种解决方案在下面描述)。

ActiveMQ Broker 重新投递(长时间重试)

这种重试机制与前两种机制有不同的特点,因为它是由代理本身管理的(而不是消息消费者或 Camel 路由引擎)。得益于其调度程序,ActiveMQ 能够延迟传递消息。此功能是代理重新交付插件的基础。重新投递插件可以拦截死信处理并重新安排失败的消息进行重新投递。失败的消息不会被传送到 DLQ,而是被安排到原始队列的尾部并重新传送给消息使用者。当总消息顺序不重要并且消费者之间的吞吐量和负载分配很重要时,这很有用。

ActiveMQ 重新传递示例

旁注——我知道,无耻的插件,但我对我关于这个主题的书感到非常兴奋。在 6 月底之前,您可以在此处以 40% 的折扣查看!并希望你喜欢它。与之前方法的不同之处在于,消息在代理消息存储中是持久的,并且它将在代理或 Camel 路由重启后继续存在,而不会影响重新传递时间。另一个优点是不会为每个重试消息阻塞线程。由于消息返回给代理,竞争消费者模式可用于将消息传递给不同的消费者。但副作用是消息顺序丢失,因为消息将被放在消息队列的尾部。此外,使用调度程序运行代理会对性能产生一些影响。这种重试机制对于长时间延迟的重试很有用,在这种情况下,您无法为每条失败的消息都阻塞线程。当您希望消息被持久化并聚集在一起以进行重新传递时,它也很有用。

请注意,与使用代理重新交付插件相比,手动实现代理重新交付逻辑更容易。您所要做的就是捕获异常并将带有 AMQ_SCHEDULED_DELAY 标头的消息发送到中间队列。一旦延迟过去,消息将被消费并重试相同的操作。您可以多次重新安排和处理同一消息,直到放弃并将该消息放入退避队列或死信队列。

标签: Java教程
地址:https://www.cundage.com/article/jcg-short-retry-vs-long-retry-apache-camel-2.html