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

可用性

  简单点来说就是系统能否正常使用。如果系统能够及时响应一个请求,则认为是可用的;如果响应时间过长或者根本不响应,则是不可用的。系统在停机或超载时是不可用的。一般用系统正常运行时长的百分比来计量系统的可用性,例如常常用N个9表示系统的可用性。故障时间秒数=(1-可用性) * 365 * 24 * 3600

描述 通俗叫法 可用性级别 年度停机时间
基本可用性 2个9 99% 87.6小时
较高可用性 3个9 99.9% 8.8小时
具有故障自动恢复能力的可用性 4个9 99.99% 53分钟
极高可用性 5个9 99.999% 5分钟

   与可用性相关的几个概念在这里稍微提一下,虽然本书并没有提及。

  RPO:(Recovery Point Obejective,恢复点目标)是指业务系统所允许的在灾难过程中的最大数据丢失量,用来衡量容灾系统的数据冗余备份能力。

  RTO:(Recovery Time Objective,恢复时间目标)是指信息系统从灾难状态恢复到可运行状态所需的时间,用来衡量容灾系统的业务恢复能力。

  第七章提到了很多导致系统不可用的因素,而且无法完全避免出现故障。可用性则要求在出现故障时可以找到缓解故障的方法,尽可能快的恢复系统。

微服务和单体式应用

  应用程序一般由两种构建方法:单体式架构、微服务架构。一般处于二者中间状态。

  单体式应用程序是指把所有组件都部署为单个单元的应用程序。一般创建各种独立的库隔离复杂性,然后把这些库编织在一起构建成一个完整的大系统。微服务则通过较大的应用程序分成较小的服务来构建,以隔离的方式执行非常细微的任务,复杂性被隔离在各个独立的微服务之内,然后通过其他微服务进行组合。

  其实两种架构是对“分而治之”思想的不同粒度的解释。单体式应用从微观的角度拆分系统,微服务从整体的角度拆分系统。由此来看,并不是拆分的越细越好。

  虽然可用性跟哪种架构没太大关联,但系统拆分的粒度不同,则提供可用性和可扩展性的方法就不同。单体式应用只有一个可部署的单元,此时提高可用性一般就是把部署单元复制多份。这个实施起来简单粗暴,还有效。但有时候不一定能奏效。比如部署单元可以复制多份,但是数据库却不能简单的复制多份,毕竟数据才是王道。

  微服务就是把单体式应用拆分为多个子系统,每个系统是独立的,子系统可以看做单体式应用按照复制的方法提高可用性。

  Akka为多个微服务通信提供相关的工具。

用有界上下文划分微服务

  其实微服务的划分方法,每个人有不同的见解。我比较倾向于作者的划分方法,也就是从业务的角度划分微服务。DDD提供了一种将应用程序划分为较小部件的方法,有界上下文就是划分的界限。但有些人倾向于从系统的角度划分微服务,其实就是将系统的各个模块抽象、独立出来成为一个子系统。比如将一组逻辑上相关的jar包封装起来对外提供服务。这一点就见仁见智吧。其实DDD可以很自然的映射到Akka的相关技术,这里就不对作者提到的方法进行阐述了,读者自行体会吧。

细粒度的微服务

  有界上下文是划分应用程序的完美起点,但有时候需要分解成更小的规则。Akka经常把界面和域分成单独的微服务,这样就可以独立地扩展双方。关于这一点就需要我们根据系统的实际情况来平衡了。但我觉得基本的出发点就是封装变化,并对其进行扩展。

集群感知路由器

  使用本地的单个actor系统构建时,路由器可以提高可扩展性。处理分布式系统时,还可以将路由器作为一个提供可用性的工具。集群感知路由器与普通路由器类似,只不过它的路由可能驻留在集群中的其他节点上,允许这些路由器在可扩展性之外提供可用性。其实路由器有点类似于注册中心,路由器负责分发消息。集群感知路由器可以自动感知节点的位置,并根据路由策略发送消息。如果一个节点出现故障,路由器可以简单的把消息路由到另外一个不通过的活跃节点上。这就额外获得了可用性。

  集群感知路由器与本地路由器的路由策略稍有不同。比如使用最小邮箱的路由器不知道集群中哪个节点的邮箱比较小。集群感知路由器通过actor的相对路径收集集群中所有符合条件的actor,与单例代理不同,它不会缓存消息,也就是消息可能丢失。

 分布式数据

  在最终一致的分布式系统中,有时候会遇到瞬态数据,其实也就是临时数据。他们不需要保存在数据库,只存在应用程序运行期间,比如用户的会话信息。瞬态信息在所有节点上保持可用性对系统来说非常重要。比如用户信息如果在新节点上不可用,系统就无法继续为该用户提供正常的服务。其实吧,我觉得如果瞬态数据非常重要,那就保存在数据库呗,实在不行保存在分布式缓存也是好的。Akka为此还单独提供了相关的组件,真是考虑周到啊。

  Akka为我们提供了一种分布式的、最终一致的存储和检索数据的方法。这种最终一致的数据类型被称为无冲突复制数据类型或CRDT(Conflict-Free Replicated Data Types)。关于这个CRDT网上的资料不是很多啊。Akka实现了称为分部署数据的CRDT,目前还不稳定。

  CRDT要求数据类型必须包含一个无冲突的合并方法,此方法具有接收两种不同状态的数据(来自集群中两个不同位置)并将其合并在一起以创建最终结果的能力。如果合并完成没有遇到冲突,那么就可以使用此数据结构来跨节点进行复制。这就是CRDT的工作原理,当一个节点收到对数据更新的命令时,它将当前状态广播给其他节点。其他节点收到更新的状态时,与自己的状态进行合并,然后存储最终结果。关于这个CRDT可以参考wikipedia。不建议使用这个东西,毕竟用一个数据库或者分布式缓存就可以搞定了,还这么麻烦进行广播。这个技术只能说明Akka的强大和复杂而已。

优雅降级

  将应用程序分解为微服务的好处之一就是可以实现优雅降级,但前提是你的系统得支持优雅降级。优雅降级需要你衡量系统的重要性、优先级,也就是说那些服务是可以降级的。熔断器模式可以实现优雅降级。比如检测外部系统的故障是一个耗时的操作,如果每个后续请求都要等待连接超时知道资源再次可用,整个系统将承担很多额外的负担。熔断器可以快速的使后续请求快速失败,可以有效减少系统的负载,便于系统故障恢复,也可以提高系统的可用性。

  其实熔断器是“分而治之”的逆向思维,有时候过渡分解不是一件好事,把检测超时这个功能向上抽象汇总,提供统一的处理方式,可以有效的减少系统的负载,带来上述的好处。看来系统并不一定是越精细越好。

 部署

  即使应用程序提高了可用性,但如果部署时仍需要关闭整个应用程序,那么可用性就会大打折扣。所以提高可用性是一件比较困难的事情。

分阶段部署/滚动重启

  导致节点在系统中不可用的最常见的原因并不是错误,而是升级!这听起来挺扯的。即使你的应用程序设计的完美,不会宕机,但如果升级的时候需要停机,那么可用性这个指标就不会太高。特别是你有100台机器需要升级版本的时候,那就更加痛苦了。如果你的版本不兼容,那么就意味着100台机器需要同时关掉,然后一台台部署。想想就觉得呵呵。当然了,如果你的自动化运维比较完善,就会省事儿很多。

  分布式系统最常用的方法就是使用分阶段部署,也成为滚动重启。滚动重启有一个比较关键的技术就是对请求进行分流,也就是可以将系统的请求暂时路由到旧有系统,当新节点可用时,再将请求回流。当然了,这只是涉及一个系统,如果涉及多个系统,那就更麻烦了。如果两个系统必须同步升级,比如接口变化了,且版本不兼容,想想是不是更加觉得呵呵?另一个关键的技术就是版本兼容了。所以可用性并不是一件容易的事儿。

猜你喜欢

转载自www.cnblogs.com/gabry/p/9183374.html