面对复杂多变的业务和运维环境,很多人搜索枯肠想要维持业务的继续运行。
然而很多时候,无论删库跑路导致企业失落所有要害业务数据,或内部施工出错挖断光缆电线,甚至某些外部或内部根底服务上一个小小的谬误配置导致半个地球范畴内的服务中断……所有这些或大或小的问题,总会让很多人慌手慌脚解决半天,还会让业务甚至企业名誉蒙受不小的影响。
尽管那句话说得好:毁坏稳态的难度越大,咱们对系统行为的信念就越强;并且只有能发现某个弱点,咱们就有了一个改良指标。
然而以往在本地部署和运行要害利用时,包含基础架构、底层硬件在内的很多因素可由企业自行掌控,因而发现并解决弱点还是好解决的(兴许须要投入大量资金和人力)。但当企业开始上云,通过云平台运行这些要害利用时,底层基础架构的治理和保护是由云平台承当的,这时又该如何解决弱点,打造更稳固、更有韧性的运维环境和应用程序?
本文将从设计思路角度登程,通知你如何进步云原生利用的韧性,确保在遇到事件后业务仍然可能稳当运行。
与近程服务和资源通信的所有应用程序必须对 临时性故障敏感。对于云中运行的应用程序尤其如此,因为其环境的性质与通过互联网建设连贯的特点,意味着更容易遇到此类问题。临时性故障包含客户端和服务霎时断开网络连接、后盾服务临时不可用,或者并发过大呈现的超时等。这些谬误通常是能够自我修复的,如果能把故障影响管制在肯定范畴内,则可将对最终用户的影响降至最低。
为什么云中会呈现临时性故障?
任何环境、任何平台或操作系统以及任何类型的应用程序都会产生临时性故障。在本地基础架构上运行的解决方案中,应用程序及其组件的性能和可用性通常由低廉且利用率有余的冗余硬件来保障。尽管此办法使故障的可能性升高,但仍可能导致临时性故障,甚至因内部电源 / 网络问题或其余劫难状况等不可预测的事件而中断。
托管型云服务(PaaS)能够跨多个计算节点应用共享资源、冗余、主动故障转移和动静资源分配,实现更高的整体可用性。然而这些环境的性质意味着更可能产生临时性故障,起因包含:
- 云环境中的许多资源是共享的,为了无效治理这些资源,云通常会严格管控对这些资源的拜访。例如,某些服务在负载回升到特定级别,或达到吞吐量比率下限时,会回绝额定连贯以便解决现有申请,并为所有现存用户维持服务性能。限度有助于为共享资源的街坊与其余租户维持服务质量。
- 云环境是应用大量商用硬件单元构建而成的。云环境将负载动静扩散到多个计算单元和基础架构组件上以取得更多性能,并通过主动回收或更换故障单元来提供可靠性。这种动态性意味着可能偶然会产生临时性故障或暂时性连贯失败。
- 在应用程序与资源及其应用的服务之间通常有多个硬件组件,包含网络基础架构,例如路由器和负载均衡器。这些附加的组件偶然会导致额定的连贯提早或临时性连贯故障。
- 客户端与服务器之间的网络情况会不断扭转,尤其是通过互联网通信时。即便在本地地位,高流量负载也可能减慢通信速度,并造成间歇性的连贯故障。
面临的挑战
临时性故障可能会对用户感知的可用性产生微小影响,即便应用程序已在所有可预感的状况下进行了全面测试。若要确保云托管的应用程序牢靠运行,应用程序必须可能应答以下挑战:
- 应用程序必须可能检测到故障的产生,并确定这些故障可能是临时性的、持久性的还是终端故障。产生故障时,不同资源可能返回不同响应,这些响应可能会依据不同操作而有所不同。例如,针对从存储读取时所产生谬误返回的响应,与针对写入存储时所产生谬误返回的响应不同。许多资源和服务都妥善制订了临时性故障的策略。然而若不提供此类信息,则很难发现故障的性质,以及故障是否是临时性的。
- 如果确定故障可能是临时性的,应用程序必须可能 重试操作,并跟踪操作重试的次数。
- 应用程序必须应用 适当的重试策略。此策略指定应用程序应该重试的次数、每两次尝试的延迟时间,以及尝试失败后执行的操作。适当的尝试次数以及每两次尝试的延迟时间通常难以确定,会依据资源类型以及应用程序自身的以后操作条件而有所不同。
韧性设计指南
以下指南将帮忙您为利用程序设计适合的临时性故障解决机制:
确定是否存在内置重试机制
- 许多服务提供 SDK 或蕴含临时性故障解决机制的客户端库。服务应用的重试策略通常是依据指标服务的性质和要求定制的。或者对于确定重试是否正确,以及在下一次尝试重试之前要期待多长时间方面,服务的 REST 接口可能会返回有用的信息。
- 如果可用,请应用内置重试机制,除非有使不同重试行为更适合的具体且明确的要求。
确定操作是否适宜重试
- 应该仅在暂时性故障,以及在从新尝试时至多有肯定胜利的可能性之下才进行重试操作。对于批示有效操作(如对不存在的项进行数据库更新,或对产生致命谬误的服务或资源的申请)的操作,从新尝试是没有意义的。
- 一般而言,只有在可能确定操作的全副影响并充沛理解情况并可进行验证时,才倡议施行重试。否则应该由调用代码来施行重试。请记住,从无法控制的资源与服务返回的谬误可能会随着工夫而演进,可能须要从新建设拜访临时性故障的检测逻辑。
- 创立服务或组件时,请思考实现谬误查看代码和音讯解决,以帮忙客户端确定是否应该重试失败的操作。特地是,批示客户端是否应该重试该操作,并在下一次重试尝试之前倡议一个适当的提早。如果构建 Web 服务,请思考返回在服务契约中定义的自定义谬误。即便通用客户端可能无奈读取这些信息,但在构建自定义客户端时它们将十分有用。
确定适当的重试计数与距离
- 优化用例类型的重试计数和距离是至关重要的。如果没有重试足够次数,应用程序将无奈实现操作,并可能经验失败;如果重试过屡次,或者重试距离过短,应用程序可能会长期占用线程、连贯和内存等资源,这将对应用程序的运行状况产生不利影响。
- 工夫距离和重试次数的适当值取决于正在尝试的操作类型。例如,如果操作是用户交互的一部分,那么距离应该很短,并且只尝试几次,以防止让用户期待响应(这会放弃关上的连贯并升高其余用户的可用性;如果操作是长时间运行或要害工作流的一部分,其中勾销和重新启动流程是费时费力的,那么在尝试和重试之间期待更长时间是适合的。
- 确定重试之间的适当距离是设计胜利策略中最艰难的局部。典型策略应用以下类型的重试距离:
(a)指数提早:应用程序在第一次重试之前短暂期待,每个后续重试的间隔时间呈指数减少。例如,在 3 秒、12 秒、30 秒后重试操作。
(b)增量距离:应用程序在第一次重试之前短暂期待,每个后续重试的间隔时间增量递增。例如,在 3 秒、7 秒、13 秒后重试操作。
(c)固定距离:应用程序每次尝试的间隔时间雷同。例如,固定每 3 秒重试操作
(d)立刻重试:有时临时性故障很短暂,可能是因为网络数据包抵触或硬件组件呈现峰值等事件。在此状况下,适宜立刻重试操作,因为如果故障在操作让应用程序组合并发送下一个申请时已革除,则操作可能会胜利。然而不应有屡次立刻重试尝试,如果立刻重试失败,应切换到备用策略,例如指数让步或回退操作。
(f)随机化:下面列出的任何重试策略都可能包含随机化,以避免客户机的多个实例同时发送随后的重试尝试。例如,一个实例可能在 3 秒、11 秒、28 秒之后重试该操作,而另一个实例可能在 4 秒、12 秒、26 秒之后重试该操作。随机化是一种有用的技术,能够与其余策略相结合。 - 个别领导准则是,为后盾操作应用指数让步策略,为交互式操作应用立刻或固定距离重试策略。在上述两种状况下,应该抉择提早与重试计数,使所有重试的提早下限都在所需的端到端提早要求范畴内。
- 思考影响重试操作的总的最大超时的所有因素的组合。这些因素包含失败连贯产生响应所破费的工夫(通常由客户端中的超时值设置)以及重试尝试和最大重试次数之间的提早。所有这些工夫的总和可能会导致较长的总体操作工夫,特地是当应用指数提早策略时,其中重试距离在每次失败后迅速增长。如果流程必须满足特定的服务水平协定 SLA,则整个操作工夫(包含所有超时和提早)必须在 SLA 定义的限度内。
- 过于激进的重试策略(距离太短或重试太频繁)可能会对指标资源或服务产生不利影响。这可能会阻止资源或服务从其重载状态中复原,并且它将持续阻塞或拒绝请求。这导致了一个恶性循环,越来越多的申请被发送到资源或服务,从而进一步升高了其恢复能力。
- 在抉择重试距离时,要思考操作的超时工夫,以防止立刻启动后续的尝试(例如,如果超时工夫与重试距离相似)。还要思考是否须要将可能的总工夫(超时加上重试距离)放弃在特定的总工夫以下。超时工夫异样短或异样长的操作可能会影响期待多长时间以及重试操作的频率。
- 应用异样的类型和它蕴含的任何数据,或者从服务返回的错误代码和音讯,来优化重试的距离和次数。例如,一些异样或错误代码(例如响应中带有 Retry-After 头的 HTTP 代码 503 Service Unavailable)可能批示谬误可能继续多长时间,或者服务曾经失败,不会响应任何后续的尝试。
防止反模式
- 在绝大多数状况下,应该防止蕴含反复重试代码层的实现。防止蕴含级联重试机制的设计,或者在波及申请层次结构的操作的每个阶段实现重试的设计,除非有要求这样做的特定需要。在这些异常情况下,请应用避免过多重试次数和延迟时间的策略,并确保了解其结果。例如,如果某个组件对另一个组件发出请求,后者再拜访指标服务,并且要对这两个调用各施行重试三次,则总共会对该服务重试九次。许多服务和资源施行内置重试机制,如果须要在较高级别施行重试,应考察如何禁用或批改此设置。
- 永远不要实现无止境的重试机制。这可能会阻止资源或服务从过载状况中复原,并导致节流和回绝连贯继续较长时间。应用无限的次数或重试,或实现一个模式(如断路器),以容许服务复原。
- 立刻重试不要超过一次。
- 防止应用惯例重试距离,特地是当拜访 Azure 中的服务和资源时,有大量的重试尝试时。在这种状况下,最优的办法是采纳具备断路能力的指数后退策略。
- 避免同一客户端的多个实例或不同客户端的多个实例在同一时间发送重试。如果可能产生这种状况,请在重试距离中引入随机化。
测试重试策略与施行
确保在尽可能宽泛的状况下全面测试重试策略实现,特地是当应用程序和它应用的指标资源或服务都处于极其负载下时。要在测试期间查看行为,能够:
- 将瞬态和非瞬态故障注入服务。例如,发送有效申请或增加检测测试申请的代码,并应用不同类型的谬误进行响应。
- 创立资源或服务的模仿,该模仿返回实在服务可能返回的一系列谬误。确保笼罩了重试策略旨在检测的所有类型的谬误。
- 如果是本人创立和部署的自定义服务,则通过长期禁用或重载该服务来强制产生瞬态谬误(当然,咱们不应该尝试重载 Azure 中的任何共享资源或共享服务)。
- 对于基于 HTTP 的 API,能够思考在自动化测试中应用 FiddlerCore 库,通过增加额定的往返工夫或更改响应(如 HTTP 状态代码、头、注释或其余因素)来更改 HTTP 申请的后果。这样就能够对故障条件的子集进行确定性测试,无论是刹时故障还是其余类型的故障。
- 执行高负载系数和并发测试,以确保重试机制和策略在这些条件下正确工作,并且不会对客户机的操作产生不利影响或导致申请之间的穿插净化。
治理重试策略配置
- 重试策略是重试策略的所有元素的组合。它定义了确定故障是否可能是临时的检测机制、要应用的距离类型(如惯例、指数后退和随机化)、理论距离值和重试次数。
- 即便是最简略的应用程序,也必须在许多中央实现重试,更简单的应用程序的每一层都必须实现重试。思考应用一个中心点来存储所有策略,而不是在多个地位硬编码每个策略的元素。例如,将距离和重试计数等值存储在应用程序配置文件中,在运行时读取它们,并以编程形式构建重试策略。这使得治理设置、批改和微调值以响应一直变动的需要和场景变得更加容易。然而,要设计零碎来存储这些值,而不是每次都从新读取配置文件,并确保在无奈从配置中取得这些值时应用适合的默认值。
- 在 Azure 云原生应用程序中,思考将用于构建运行时重试策略的值存储在服务配置文件中,这样就能够在不重启应用程序的状况下更改它们。
- 利用客户端 API 中提供的内置或默认重试策略,但只在它们适宜的场景应用。这些策略通常是通用的。在某些场景中,它们可能是所有必须的,但在其余场景中,它们可能不会提供所有选项来满足特定需要。通过测试确定最合适的值,咱们必须理解设置将如何影响应用程序。
记录和跟踪瞬态和非瞬态故障
- 作为重试策略的一部分,包含异样解决和记录重试尝试时的其余检测。尽管偶然呈现短暂的故障和重试是能够预期的,并且并不表明有问题,但定期的和一直减少的重试次数通常是一个可能导致故障的问题的指示器,或者以后正在升高应用程序的性能和可用性。
- 将瞬态故障记录为正告项而不是谬误项,以便监控零碎不会将它们检测为可能触发谬误警报的应用程序谬误。
- 思考在日志条目中存储一个值,该值批示重试是由服务中的节流引起的,还是由其余类型的谬误(如连贯失败)引起的,以便在剖析数据时对它们进行辨别。节流谬误数量的减少通常表明应用程序存在设计缺点,或者须要转向提供专用硬件的优质服务。
- 思考测量和记录蕴含重试机制的操作所破费的总工夫。这是暂时性谬误对用户响应工夫、解决提早和应用程序用例效率的总体影响的一个很好的指示器。还要记录产生的重试次数,以便理解影响响应工夫的因素。
- 思考实现一个遥测和监控零碎,当失败的数量和比率、均匀重试次数或操作胜利所需的总工夫减少时,该零碎能够收回警报。
治理继续失败的操作
- 在某些状况下,每次口头都会失败,思考如何解决这种状况是至关重要的。
- 只管重试策略将定义一个操作应该重试的最大次数,但它不会阻止应用程序以雷同的重试次数再次反复该操作。例如,如果一个订单解决服务因为一个致命谬误而失败,使其永恒进行操作,重试策略可能会检测到连贯超时,并认为这是一个临时的谬误。代码将重试指定次数的操作,而后放弃。然而当另一个客户下订单时,该操作将再次尝试——即便每次都必定会失败。
- 为了避免对一直失败的操作进行一直重试,思考实现断路器模式。在此模式中,如果指定工夫窗口内的失败次数超过阈值,则申请将立刻作为谬误返回给调用者,而不尝试拜访失败的资源或服务。
- 应用程序能够周期性地测试服务(断断续续的,申请之间的距离很长),以检测服务何时可用。适当的距离取决于场景,例如操作的要害水平和服务的性质,可能是几分钟到几个小时之间的任何工夫。在测试胜利时,应用程序能够恢复正常操作,并将申请传递给新复原的服务。
- 与此同时,能够退回到服务的另一个实例(可能在不同的数据中心或应用程序中),应用提供兼容(可能更简略)性能的相似服务,或者执行一些代替操作,心愿该服务很快可用。例如,能够将服务申请存储在队列或数据存储中,稍后再重放它们。否则咱们可能会将用户重定向到应用程序的另一个实例,升高应用程序的性能,但依然提供可承受的性能,或者只是向用户返回一条音讯,批示该应用程序目前不可用。
其余的思考
- 在决定重试次数和策略重试距离的值时,请思考服务或资源上的操作是否是长时间运行或多步骤操作的一部分。当一个操作步骤失败时,弥补所有其余曾经胜利的操作步骤可能是艰难的或低廉的。在这种状况下,很长的距离和大量的重试是能够承受的,只有它不通过持有或锁定稀缺资源来阻塞其余操作。
- 请思考重试同一操作是否可能导致数据不统一。如果反复多步骤流程的某些局部,且操作不是幂等的,则可能导致不统一。例如,递增值的操作如果反复,将产生有效的后果。如果无奈检测到反复的音讯,反复将音讯发送到队列的操作可能会导致音讯使用者中呈现不统一。要避免这种状况,请确保将每个步骤设计为幂等操作。
- 思考将要重试的操作的范畴。例如,在蕴含多个操作的级别上实现重试代码可能更容易,如果其中一个操作失败,则重试所有操作。然而,这样做可能会导致幂等问题或不必要的回滚操作。
- 如果抉择一个蕴含多个操作的重试范畴,那么在确定重试距离、监督所破费的工夫以及收回失败警报之前,请思考所有操作的总提早。
在为云原生利用思考韧性设计时,请务必谨慎考虑重试策略可能会如何影响共享应用程序中的街坊和其余租户。激进的重试策略可能导致其余用户以及共享资源和服务的应用程序呈现越来越多的刹时谬误。
同样,咱们的应用程序可能会受到资源和服务的其余用户所实现的重试策略影响。对于要害工作应用程序,咱们能够决定应用不共享的高级服务。这为咱们提供了更多的负载管制以及相应资源和服务的节流,这有助于证实额定的老本是正当的。
遵循上述思路,并联合具体情况进行调整,咱们就能够顺利设计出具备足够韧性的云原生利用架构。
心愿本文对你能有所帮忙。如果对这个话题感兴趣,那么敬请期待后续公布的更多系列文章,咱们将持续从重试模式、断路器模式等角度深入探讨相干机制的实现思路和办法。更多精彩敬请期待!