《Akka应用模式:分布式应用程序设计实践指南》读书笔记7

简介: 容错  容错绝对是分布式系统最难搞定的事儿,至少我这样认为,因为意外总是会发生。  处理故障在许多方面意味着要放弃全局一致性。Akka是基于不粗要调用方负责处理故障的想法而建立的。它主张由发生故障的actor负责处理问题,在actor不能处理的情况下,会向其“监督者”寻求帮助。

容错

  容错绝对是分布式系统最难搞定的事儿,至少我这样认为,因为意外总是会发生。

  处理故障在许多方面意味着要放弃全局一致性。Akka是基于不粗要调用方负责处理故障的想法而建立的。它主张由发生故障的actor负责处理问题,在actor不能处理的情况下,会向其“监督者”寻求帮助。由于Actor模型基于消息机制设计,就意味着系统的很多部分都是异步的,异步又进一步导致出现故障时解决问题的难度。而Akka有一套完整的机制,将故障尽量限制在对应的actor上,缩小出现故障的影响范围和降低解决故障的成本和时间。但·Akka只是提供了故障恢复的机制,具体怎么恢复还是需要我们自己设计。

故障类型

  在之前我也说过,分布式系统有很多不确定性,会出现各种意外,对意外分门别类总是有好处的。

异常

  不管是单机系统,还是分布式系统,系统故障中最常见的类型之一。其实自从学了Scala,觉得传统的异常机制不太完美。因为它以一种奇怪的方式改变了程序的流程。而且绝大部分你总是会发现没有处理的异常,也就是说,异常变成了程序异常退出的一个出口,而异常一旦有很多个,就意味着程序“千疮百孔”,各种漏洞。这样的结果很可能导致系统释放资源的代码没有资源,造成资源泄露或其他意想不到的问题。所以我更喜欢用一个Try来封装所有的异常。

JVM致命错误

  这就比较难办了,很多时候都是任期发生,然后找出问题再解决。需要注意的是,Try无法处理这种会使系统崩溃的严重错误,比如内存溢出。看看Try的源码就知道为啥了。

外部服务故障

  系统往往需要与外部系统进行交互,外部API出现问题也是比较头疼的。因为问题不可控,我们只能选择接受,而无法解决。但意识到这种问题的存在非常重要。

不符合服务等级协议(SLA)

  其实这就比较主观了,毕竟SLA还是可以定的比较宽松的,^_^。如果一个系统总是能返回一个正确的结果,但却不一定能保证在合理的时间内返回,这就比较尴尬了。最好的系统仍然可能成为高负载的情况下的受害者,因为它无法达到预定义的服务等级协议(SLA)。这其实还挺尴尬的,有时候SLA还会变化,就更麻烦了。

操作系统和硬件级故障

  这个吧,只能买好一点的机器。。。不过还是可以指定适当的预防措施的。

故障隔离

  我觉得这是处理故障的最正确的一个设计理念。那就是不要试图去消除全部的故障,而是承认其存在,将其限定在合理的范围内,隔离其影响范围。幸运的是,Akka提供了相关的工具。

舱壁模式

  这种模式对于我来说还是比较新颖的,因为我一直以为船舶一旦触礁,就会有大量的水流到船体内,然后就挂了。Akka提供了以actor层次结构形式实现的舱壁模式工具。也就是actor内部的错误不会蔓延,只影响其自身的功能。如果需要,还可以进一步分解,让特定actor只处理特定时间段内的请求,这样可以将请求中的故障隔离在请求发生的时间段内。

  这种设计理念我还是很喜欢的,毕竟很多时候,导致出现故障的原因,也许只是遇到了某个异常的请求,忽略这个请求,简单的重启继续后续消息的处理也许是非常合理的。

优雅降级

  这在互联网的架构博客中经常会被提及。其实刚开始我对它的理解比较粗浅,什么优雅降级,不就是let it go吗?所谓的优雅降级,就是牺牲一部分系统功能,使当前最重要的业务功能可用。优雅降级可以避免使这鞥个站点发生故障,只让其中一部分不可用,其他功能不受故障影响。这种降级,有时候是系统自动发生的,有时候是人工启动的。比如在业务高峰期,系统资源不足,可以选择性的将部分功能下线,以确保最核心的业务功能可用,就是所谓的丢车保帅。

使用Akka集群隔离故障

  Akka内置了舱壁模式,使其更加透明。集群分片某种意义上来说,就是对actor进行故障隔离。另外一个好处就是它可以自动将失败的节点上的actor重新分配给其他节点。

使用熔断器控制故障

  熔断器通常用来解决高负载情况下系统的故障隔离的。简单点来说,就是当系统处于高负载时,为了避免后续的消息使情况进一步恶化,成为压死骆驼的最后一根稻草,自动的使所有调用失败,甚至不会尝试处理调用请求。也就是尽快的通知调用方,调用失败了。故障恢复或者负载变低时,调用恢复正常。熔断器提供 一个让发生故障的系统在一段时间内恢复原有功能的方法。

  在预先设置好的时间段过去之后,熔断器进入“半开”状态。此时,进入熔断器的第一个调用将被处理,如果调用成功,则熔断器进入关闭状态,系统恢复正常运行。如果第一个调用还是失败,则熔断器返回到“打开”状态,继续使所有的调用快速失败。如果回到“打开”状态,则在接下来的一段时间内都保持打开状态。

  幸运的是Akka内置了熔断器CircuitBreader,使用还是比较方便的。

 故障处理

  前面已经对故障的类型做了分类,也对故障隔离和故障控制做了介绍,但没有说如何解决故障。Akka是设计理念是“let it crash”。从崩溃中恢复才是弹性系统应该具备的能力。首先认识的故障的发生,才能正面的解决和规避它。

异常处理

  异常一般分为非致命异常和致命异常。当然了是否致命是对整个系统来说的,不是对某个代码、函数或者actor来说的。

  在单个actor中,可以简单的处理可预测的故障,比如用try catch快封装;当异常不能被它的actor处理时,就会把异常发送给上层的监督者,由监督者决定如何处理。幸运的是Akka内置了几种常见的恢复策略。OneForOneStrategy提示监督者将恢复逻辑应用于发生故障的actor,其他actor不受影响;AllForOneStrategy告诉监督者将恢复逻辑应用于所有的子actor。恢复逻辑一般就是重启,actor重启的代价比较小,只需要做好相关的业务初始化代码即可。Decider是一个partial函数,它根据异常类型决定采取的应对方式。Decider将异常类型映射到以下执行。

  Resume。告诉发生故障的actor继续处理消息,即忽略发生故障的消息。

  Restart。告诉发生故障的actor应该重启。

  Stop。告诉发生故障的actor应该被停止。比如临时、一次性的actor,失败后不再需要,就可以停止。

  Escalate。将故障升级。其实就是把异常向上汇报,由监督者的监督者处理。

  其实选择合适的异常处理策略还是比较难的,因为异常就是意外,很难预测。我们所能做的就是对已知的故障进行应对,无法预见的就只能交给后期版本迭代了。

  JVM致命错误怎么办呢?Akka自身没有现成的工具可以做到这一点。但可以引入第三方工具,比如ConductR或者supervisor。当检测到对应JVM退出时,简单的重启。但还可以优化一下,比如在应用程序中设置一个回退点。在Akka中,我们可以选择将消息持久化,从某个时间点开始,回放消息。当然了这需要系统具备至少一次的交付机制。

外部服务的故障处理

  其实外部服务的故障我们往往没法解决,只能将其隔离,限制影响范围。可以将外部服务系统是为一个有界的上下文,为它创建一个包装器包装它。简单情况下,用一个actor封装对应的操作就好了。不过还可以用AtLeastOnceDelivery机制来确保外部服务恢复时,消息被重新发送处理。

  作者提到过渡使用AtLeastOnceDelivery机制表明我们正在尝试实现全局一致性,这一点对我的启发挺大的。其实我们在使用某个技术细节的时候,往往只考虑它带来的好处,而看不到隐藏的弊端。再加上程序员都是分流派的,互相怼来怼去,就更加夸大某个技术的好处,而选择性忽略其问题。技术要合理的使用。AtLeastOnceDelivery机制最适合调用流水线的两端,不要在管道的中间阶段使用它。

结论

  基于actor的分布式系统发生系统故障的可能性非常低,同事比其他类型的系统更容易从故障中恢复。这一点我深信不疑,看看我开源的代码就知道原因了。不过在分布式系统中,对于故障处理是一件非常麻烦的事情,因为它有可能占用系统设计、开发总时间的50%,甚至更多。

目录
相关文章
|
3月前
|
SQL 数据建模 BI
【YashanDB 知识库】用 yasldr 配置 Bulkload 模式作单线程迁移 300G 的业务数据到分布式数据库,迁移任务频繁出错
问题描述 详细版本:YashanDB Server Enterprise Edition Release 23.2.4.100 x86_64 6db1237 影响范围: 离线数据迁移场景,影响业务数据入库。 外场将部分 NewCIS 的报表业务放到分布式数据库,验证 SQL 性能水平。 操作系统环境配置: 125G 内存 32C CPU 2T 的 HDD 磁盘 问题出现的步骤/操作: 1、部署崖山分布式数据库 1mm 1cn 3dn 单线启动 yasldr 数据迁移任务,设置 32 线程的 bulk load 模式 2、观察 yasldr.log 是否出现如下错
|
3月前
|
机器学习/深度学习 存储
DeepSeek进阶开发与应用4:DeepSeek中的分布式训练技术
随着深度学习模型和数据集规模的扩大,单机训练已无法满足需求,分布式训练技术应运而生。DeepSeek框架支持数据并行和模型并行两种模式,通过将计算任务分配到多个节点上并行执行,显著提高训练效率。本文介绍DeepSeek中的分布式训练技术,包括配置与启动方法,帮助用户轻松实现大规模模型训练。数据并行通过`MirroredStrategy`同步梯度,适用于大多数模型;模型并行则通过`ParameterServerStrategy`异步处理大模型。DeepSeek简化了分布式环境配置,支持单机多卡和多机多卡等场景。
|
6月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
109 5
|
6月前
|
监控
Saga模式在分布式系统中保证事务的隔离性
Saga模式在分布式系统中保证事务的隔离性
123 4
|
7月前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
7月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
182 4
|
8月前
Saga模式在分布式系统中如何保证事务的隔离性
Saga模式在分布式系统中如何保证事务的隔离性
133 7
|
8月前
|
存储 NoSQL Java
分布式session-SpringSession的应用
Spring Session 提供了一种创建和管理 Servlet HttpSession 的方案,默认使用外置 Redis 存储 Session 数据,解决了 Session 共享问题。其特性包括:API 及实现用于管理用户会话、以应用容器中性方式替换 HttpSession、简化集群会话支持、管理单个浏览器实例中的多个用户会话以及通过 headers 提供会话 ID 以使用 RESTful API。Spring Session 通过 SessionRepositoryFilter 实现,拦截请求并转换 request 和 response 对象,从而实现 Session 的创建与管理。
226 0
分布式session-SpringSession的应用
|
8月前
|
存储 NoSQL Java
分布式session-SpringSession的应用
Spring Session 提供了一种创建和管理 Servlet HttpSession 的方案,默认使用外置 Redis 存储 Session 数据,解决 Session 共享问题。其主要特性包括:提供 API 和实现来管理用户会话,以中立方式替换应用程序容器中的 HttpSession,简化集群会话支持,并在单个浏览器实例中管理多个用户会话。此外,Spring Session 允许通过 headers 提供会话 ID 以使用 RESTful API。结合 Spring Boot 使用时,可通过配置 Redis 依赖和支持缓存的依赖实现 Session 共享。
149 0
分布式session-SpringSession的应用

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等