目录
重试是微服务中非常重要的设计概念,不同软件平台都有自己的重试策略,这里做下简单对比,可以做以后的设计参考。
1、微软云重试策略
这里是重试的策略的原文:重试设计模式
1.1、重试的应用场景:
•如果故障可能会持续很长时间,应用程序尝试重复一个可能失败的请求可能会浪费时间和资源。(重试次数限制,本地缓存加上智能开关如果某个接口连续重试三次还失败就不再重试)
•用于处理非瞬时故障导致的故障,例如应用程序业务逻辑错误导致的内部异常。
•作为解决系统中可伸缩性问题的替代方案。如果应用程序遇到频繁的忙故障,这通常是正在访问的服务或资源应该扩展的迹象。
备注:对于大多数Azure内置的重试机制,目前无法对不同类型的错误或异常应用不同的重试策略。我们应该配置一个最佳平均性能和可用性的策略。微调策略的一种方法是分析日志文件,查找到发生的故障的类型,并作出微调。
1.2 不同组件的重试策略:
Retry guidance for Azure services | ||||
Service | Retry capabilities | Policy configuration | Scope | Telemetry features |
Native in ADAL library | Embedded into ADAL library | 基本是超时重试。 (1)实时要求高,重试三次每次相隔500ms.比如Interactive, UI,or foreground。 (2)实时要求不高,重试5次,时间间隔安单位时间(秒/分钟)倍数递增,也就是指数后退方式重试。.比如Background or batch。示例: Attempt 1 - delay 0 sec Attempt 2 - delay ~2 sec Attempt 3 - delay ~6 sec Attempt 4 - delay ~14 sec Attempt 5 - delay ~30 sec | None | |
Native in service | Non-configurable | 超时重试:设置单位时间(秒/分钟)内最大重试次数 | TraceSource | |
Data Lake Store | Native in client | Non-configurable | Individual operations | None |
Native in client | Programmatic | Azure事件中心客户端库中的重试行为由EventHubClient类上的RetryPolicy属性控制。当Azure Event Hub返回瞬态EventHubsException或OperationCanceledException时,默认策略将以指数后退方式重试。事件集线器的默认重试策略是最多重试9次,指数后退时间最长为30秒。 | None | |
Native in client SDK | Programmatic | 不同的语言重试策略是不同的.比如java 重试接口:RetryDecision getRetryDecision(int currentRetryCount, TransportException lastException); 明显是根据异常类型来重试,lastException就是导致重试发生的最新异常,但是终端异常不会发生重试。 | None | |
Native in client | Programmatic | 连接的超时重试 | TextWriter | |
Native in client | Programmatic | 默认 超时原因为 5xx or 408 ,才会重试。 | ETW or Custom | |
Native in client | Programmatic | 同Event Bus 重试 | ETW | |
Native in client | Programmatic | Client | None | |
Declarative and programmatic | 同Event Bus 重试 | Custom | ||
Native in client | Programmatic | Global per AppDomain | None | |
Native in client | Programmatic | Global per AppDomain | None | |
Native in client | Programmatic | Client and individual operations | TraceSour |
1.3、重点看下 :REST接口的重试策略
访问Azure或第三方服务时,请考虑以下事项:
1、使用系统化的方法来管理重试,可以编写可重用的代码,这样我们就可以在所有客户端和所有解决方案中使用的方法保持一致。
2、如果目标服务或客户端没有内置的重试机制,请考虑使用Polly等重试框架来管理重试。这将帮助您实现一致的重试行为,并可能为目标服务提供适当的默认重试策略。如果服务不依赖异常来表示暂时性失败,我们可能需要为具有非标准行为(Polly为标准)的服务创建自定义重试代码, 或者也可以使用响应回复来管理重试行为。
3、实时业务校验逻辑还取决于我们用来调用REST接口时使用的客户端API。有些客户端(例如较新的HttpClient类)不会对某些已完成的请求,但是HTTP状态是失败的代码的抛出异常。
4、从服务返回的HTTP状态代码可以帮助客户端判断故障是否是暂时的。我们可能需要以请求的响应状态代码来检查客户端或重试框架生成的异常,或确定对应的异常类型。以下HTTP代码通常表示重试是适当的:
408请求超时
429请求太多
500内部服务器错误
502坏网关
503服务不可用
504网关超时
5、如果我们的重试逻辑基于异常,则以下情况通常表示无法建立连接的瞬时故障:
WebExceptionStatus.ConnectionClosed
WebExceptionStatus.ConnectFailure
WebExceptionStatus.Timeout
WebExceptionStatus.RequestCanceled
6、在服务不可用状态的情况下,服务可以在重试响应标头或其他自定义标头中指示适当的延迟。服务也可以自定义消息头作为附加信息,或者嵌入到响应的内容中。
7、对于客户端错误(4xx范围内的错误)的状态代码,不要重试,除非408请求超时和429个请求过多。
8、在一系列条件下,如不同的网络状态和不同的系统负载,彻底测试您的重试策略和机制。
9、异常处理参考:exception
2、qubole的重试策略
Qubole是一家数据分析自动化服务商,在亚马逊、微软、Google和Oracle云平台上构建了大数据分析的自助平台,提供一种云端基础设施,进行数据分析,还可以把数据存储在公共云端,而不需要建立数据中心。
一个HTTP请求在完成之前会通过多个组件,如DNS服务器、负载平衡器、应用服务器等。任何组件都可能在传输过程中产生错误。Qubole建议遵循重试策略以增加请求成功的机会。QuboleSDK 已经实现的重试策略。
2.1 、服务器端错误(5xx系列)
Qubole建议客户端 在服务器端返回以下错误码的情况下重试请求
Status Code | Error Code | Retry? |
503 | Service Unavailable | Yes |
Other 5xx errors | _ | Yes (only for GET requests) |
2.2、客户端错误(4xx系列)
Qubole建议您在返回以下客户端错误代码时重试请求。
Status Code | Error Code | Retry? |
429 | Too Many Requests | Yes |
449 | Retry | Yes (only for GET requests) |
Other 4xx errors | _ | Yes (only for GET requests) |
2.3 使用指数回退重试失败的请求
如果您的请求返回错误,如429(请求太多)或503(服务不可用),您需要在延迟后重试,以增加成功响应的机会。对于重试次数和每次重试之间的间隔,Qubole建议使用以下配置。
Parameter | Description | Value |
建议重试次数 | 建议的最大重试次数 | 7 |
基本延迟间隔 | 尝试第一次重试的时间间隔 | 10 seconds |
指数退避因子 | 上次重试后延迟间隔增加的系数。例如,在第一次尝试之后,应用程序等待20秒(2*10秒),然后重试请求。这将增加到40秒(2*20秒),依此类推。 | 2 |
3、servicenow重试策略
ServiceNow这家公司提供的重点服务是ITSM(IT Service Management,IT服务管理)和ITOM(IT Operation Management,IT运维管理)的SaaS化业务,在美国成熟的IT市场中获得了大型企业用户对其IT运维产品的认可,在IT服务这个细分赛道中超越了传统IT运维服务提供商,成为了几百亿美元市值的SaaS明星公司。
servicenow的重试定义:原文
当请求遇到临时问题(如网络故障或请求速率限制)时,可以自动重试失败的请求。设置重试策略以防止再次手动触发该请求。
重试策略可以是以下几种情况:
- 可以在http的消息头、响应状态、消息体、 异常、 以及http方法(POST/GET等)上 创建重试策略,以支持 连接超时或者失败的请求重试。
- 可应用于给定连接别名(比如数据库)的所有操作。
- 直接应用于流程中的某个步骤
定义重试策略应该包含的要素:
- 重试必须满足的条件。
- 重试等待的时间间隔。
- 重试的最大次数。
下面是一个重试策略的样例:
HTTP方法是GET,错误是连接超时
HTTP方法是GET,状态代码是429
满足条件时,最多重试三次。重试尝试之间的时间间隔呈指数级增加。本例中的时间间隔为10秒、20秒和40秒。
4、amazon重试策略
官方文档没有给出明确的定义规则,这里只能找到SDK的手册:
可以看一下 ClientConfiguration的官方文档。它有很多方法和参数来调整重试逻辑。以下是最重要的:
- withMaxErrorRetry 设置失败请求的最大重试尝试次数(例如:来自服务的5xx个错误响应)
- withRequestTimeout 设置请求的超时时间量(毫秒)[…]
- withThrottledRetries 重试限制是一种功能,当大量请求失败且重试不成功时,可以智能地限制重试尝试[…](断路器)
- withRetryPolicy 退避策略,它允许您选择RetryPolicy并更改:
- BackoffStrategy 提供自定义退避策略,用于控制重试时间间隔
RetryCondition 请求失败并重试的前提条件
maxErrorRetry 遇到异常的最大重试次数
honorMaxErrorRetryInClientConfig 是否尊重上述配置
看下退避策略的参数:
originalRequest - 正在执行的原始请求对象。出于性能原因,此对象不支持防擅改,调用方不应尝试修改其数据。.
exception - 失败请求的异常,表示为AmazonClientException对象。有两种类型的异常将传递给此方法:
- AmazonServiceException ( AmazonClientException子类)表示服务端的错误
- AmazonClientException 客户端的I/O 异常.
- 任何其他异常都会被视为意外失败,并且会立即抛出而不进行任何重试。
retriesAttempted - 尝试当前请求的次数
5、dubbo的集群策略
dubbo 的重试机制是和 它的集群策略以及负载均衡策略融合在一起的,下面是dubbo 的成熟度模型。可以看到,dubbo 相对于其他的软件平台,重试机制相对比较弱。参考文献:[1][2]
Feature | Maturity | Strength | Problem | Advise | User |
Failover Cluster | Stable | 失败自动切换,当出现失败,重试其它服务器,通常用于读操作(推荐使用) | 重试会带来更长延迟 | 可用于生产环境 | Alibaba |
Failfast Cluster | Stable | 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作 | 如果有机器正在重启,可能会出现调用失败 | 可用于生产环境 | Alibaba |
Failsafe Cluster | Stable | 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作 | 调用信息丢失 | 可用于生产环境 | Monitor |
Failback Cluster | Tested | 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作 | 不可靠,重启丢失 | 可用于生产环境 | Registry |
Forking Cluster | Tested | 并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作 | 需要浪费更多服务资源 | 可用于生产环境 | |
Broadcast Cluster | Tested | 广播调用所有提供者,逐个调用,任意一台报错则报错,通常用于更新提供方本地状态 | 速度慢,任意一台报错则报错 | 可用于生产环境 |
dubbo:reference接口配置的重试参数如下:
服务消费者引用服务配置。对应的配置类: org.apache.dubbo.config.ReferenceConfig
属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 |
timeout | timeout | long | 可选 | 缺省使用<dubbo:consumer>的timeout | 性能调优 | 服务方法调用超时时间(毫秒) | 1.0.5以上版本 |
retries | retries | int | 可选 | 缺省使用<dubbo:consumer>的retries | 性能调优 | 远程服务调用重试次数,不包括第一次调用,不需要重试请设为0 | 2.0.0以上版本 |
connections | connections | int | 可选 | 缺省使用<dubbo:consumer>的connections | 性能调优 | 对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数 | 2.0.0以上版本 |
loadbalance | loadbalance | string | 可选 | 缺省使用<dubbo:consumer>的loadbalance | 性能调优 | 负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用 | 2.0.0以上版本 |
6、spring retry
先看原文中一段关于重试的说明:
Failures are inherently either retryable or not. If the same exception is always going to be thrown from the business logic, it does no good to retry it. So do not retry on all exception types. Rather, try to focus on only those exceptions that you expect to be retryable. It is not usually harmful to the business logic to retry more aggressively, but it is wasteful, because, if a failure is deterministic, you spend time retrying something that you know in advance is fatal.
失败本质上要么是可重试的,要么是不可重试的。如果业务逻辑总是抛出同一个异常,那么重试是没有好处的。所以不要对所有异常类型重试。相反,试着只关注那些可以重试的异常。频繁的重试通常不会对业务逻辑有害,但会造成资源浪费,因为如果失败已经是确定的了,那么我们还去重试,注定是浪费时间。
重试应该包含 无状态重试 和 有状态重试。无状态重试比较极端,使用应该是比较谨慎的。这里重点要讨论的是有状态重试。在spring retry 中有状态重试的策略还是比较灵活的。
6.1 重试策略定义
该策略定义了对指定的异常进行若干次重试。默认情况下,对Exception异常及其子类重试3次。如果创建SimpleRetryPolicy并指定重试异常map,可以选择性重试或不进行重试。
执行一次待执行操作,若出现异常后不进行重试。
异常后一直重试直到成功。
在执行execute方法时从open操作开始到调用TimeoutRetryPolicy的canRetry方法这之间所经过的时间。这段时间未超过TimeoutRetryPolicy定义的超时时间,那么执行操作,否则抛出异常。
5、ExceptionClassifierRetryPolicy 策略
根据产生的异常选择重试策略。通过PolicyMap定义异常及其重试策略。比如NullPointerException采用NeverRetryPolicy策略,而TimeoutException采用AlwaysRetryPolicy。
用户指定一组策略,随后根据optimistic选项来确认如何重试。
6.2 回退策略
基本上分为三种:固定时间退避、随机时间退避、指数退避策略:
指数退避策略(立即重试还是等待一会再重试)
不设置参数时,默认使用FixedBackOffPolicy(固定时间退避),重试等待1000ms
只设置delay()属性时,使用FixedBackOffPolicy,重试等待指定的毫秒数
当设置delay()和maxDealy()属性时,重试等待在这两个值之间均态分布(随机时间退避)
使用delay(),maxDealy()和multiplier()属性时,使用ExponentialBackOffPolicy(指数退避策略)
当设置multiplier()属性不等于0时,同时也设置了random()属性时,使用ExponentialRandomBackOffPolicy
Recover:
这是一个 “兜底”处理方法,当重试次数耗尽依然出现异常时,执行此异常对应的@Recover方法。 异常类型需要与Recover方法参数类型保持一致,recover方法返回值需要与重试方法返回值保证一致
CircuitBreaker 用于方法,实现断路器模式:
include 指定处理的异常类。默认为空
exclude指定不需要处理的异常。默认为空
vaue指定要重试的异常。默认为空
maxAttempts 最大重试次数。默认3次
openTimeout 配置熔断器打开的超时时间,默认5s,当超过openTimeout之后熔断器电路变成半打开状态(只要有一次重试成功,则闭合电路)
resetTimeout 配置熔断器重新闭合的超时时间,默认20s,超过这个时间断路器关闭