《微服务设计》,Building Microservices,作者Sam Newman,译者崔力强、张骏,人民邮电出版社,2016年。
笔记中有些内容直接引用原书。
================================================================
第十一章规模化微服务
1.故障无处不在
要假定故障会发生,以这种想法来处理每一件事情,为故障做好准备。
2.多少是太多
不同的系统对容忍的故障程度、系统的响应速度等都是不一样的,需要根据这些因素选择技术,需要让客户了解不同级别的服务对应的成本。
响应时间/延迟。可以用不同数量的用户来测量。“我期望这个网站,当每秒处理200个并发连接时,90%的响应时间在2秒以内。”
可用性。服务能出现故障吗?是24/7服务吗?
数据持久性。多大比例的数据丢失是可接受的?数据应该保存多久?
3.功能降级
功能降级的需求要从业务角度出发。
4.架构性安全措施
架构性安全性措施是一些组合的模式,确保如果事情真的出错了,不会引起严重的级联影响。举了一个之前网站由于下游服务响应缓慢导致级联故障的例子,采取的措施是:正确的设置超时;实现舱壁隔离不同的连接池;实现一个断路器。
5.反脆弱的组织
《反脆弱》一书的作者Nassim Taleb认为事物实际上受益于失败和混乱。Netflix通过引发故障来确保其系统的容错性。Netflix开源了混乱猴子(Chaos Monkey)、混乱大猩猩(Chaos Gorilla)以及延迟猴子(Latency Monkey)项目,它们分别用于模拟关闭服务器、关闭可用区以及网络延迟。让软件拥抱和引发故障,从失败中学习。
超时。下游服务超时,等待太长来决定调用失败,整个系统会变慢。太短会让正常工作的调用被认为失败。没有超时,一个宕掉的下游系统会让整个系统挂起。要给所有跨进程调用设置超时,并选择一个默认时间。超时发生,记录日志。
断路器。当对下游资源的请求发生一定数量的失败后,打开断路器,请求会迅速失败。过段时间再发送检查,如果下游服务恢复正常则关掉断路器。一些断路器实现:Netflix的Hystrix库(基于JVM),.NET的Polly,Ruby的circuit_breaker mixin。
舱壁。舱壁通过隔离避免级联故障。在第4节的例子中,就是对不同服务使用了不同的连接池来隔离。还可以使用关注点分离,通过把功能分离成独立的微服务,隔离故障影响。断路器也算蜜蜂舱壁的一种自动机制。
隔离。服务间加强隔离,最好能做到上游服务可以允许下游服务离线。
6.幂等
多次执行同一操作,与执行一次该操作效果相同。使消息的处理成为幂等能减少很多工作量。
7.扩展
扩展系统的原因:处理失败;提升性能。
更强大的主机。这是垂直扩展。
拆分负载。单服务单主机模式扩展为多服务多主机模式。
分散风险。不要把鸡蛋放在一个篮子里。例如,不要把多个服务放在一台主机上。要考虑SAN的故障,它会引起所有虚拟机不可用。不要让所有服务运行在同一个数据中心的同一个机架上。了解云服务商的SLA(Service-Level Agreement)保证,确保和自己的需求相匹配。
负载均衡。使用负载均衡器,有硬件有软件(如mod_proxy)。能提供SSL终止功能(将HTTPS连接在内部转换为HTTP连接)的能简化单个主机运行实例的配置。负载均衡器的配置要和服务的配置一样通过版本管理系统管理起来,要能自动化应用。
基于worker的系统。该系统也可以像负载均衡一样分担负载降低脆弱性。通过待办作业列表来分配作业到不同的worker,需要一个持久化的消息代理或Zookeeper。
重新设计。前期将精力放在更重要的事情上,当确实有大量负载来了,再去考虑解决,必要时重新设计。
8.扩展数据库
服务的可用性和数据的可用性。要意识到二者的区别。
扩展读取。通过主节点副本可以做到扩展读取,不过建议先采用缓存(简单并且性能改善显著),其次才考虑副本。
扩展写操作。数据分片解决扩展写。看看Cassandra、Mongo或者Riak这样的数据库系统。
共享数据库基础设施。可能引起单点故障。
CQRS。命令查询职责分离(Command-Query Responsibility Segregation)模式,系统一部分负责获取修改状态的请求命令并处理它,另一部分负责处理查询。内部用于处理命令和查询的模型是完全独立的,可以使用不同的服务或在不同的硬件实现,可以使用不同的数据存储,因此扩展性强。
9.缓存
客户端、代理和服务器端缓存。代理缓存可以用反响代理或CDN;服务器端缓存可以用Redis或Memcache。
HTTP缓存。cache-control指令,Expires头部,实体标签Etag。它们的使用可以参考《REST实战》。
为写使用缓存。有爆发式写操作或同样数据可能被写入多次,后写式缓存很有作用。
为弹性使用缓存。在服务不可用时可以使用缓存代替一些服务。
隐藏源服务。缓存消失时,如果源服务无法应对大量请求,则不要请求。可以由源服务在后台重建缓存,也可以原始请求快速失败,避免级联故障。
保持简单。避免太多地方使用缓存。
缓存中毒:一个警示。需要了解数据从数据源到终点的完整缓存路径,从而真正理解它的复杂性以及使它出错的原因。
10.自动收缩
可以根据负载趋势变化来触发自动收缩,以响应负载。另外,自动伸缩被更多应用于响应故障。
11.CAP定理
一致性(consistency)、可用性(availability)、分区容忍性(partition tolerance),三个中最多只能保证两个。
牺牲一致性。
牺牲可用性。
牺牲分区容忍性。
AP还是CP。需要根据具体情况权衡。
这不是全部或全不。不同的服务可以是不同的性质,有的是AP,有的是CP,并不是整个系统中的所有服务都保持一致。
真实世界。在真实世界中,无论系统本身如何一致,都难以做到和真实世界的一致。因此,很多情况下,AP系统是最终正确的选择。
12.服务发现
服务实例要能够注册,其它服务要能够找到已经注册的实例。
DNS。可以使用DNS解决方案。更新DNS条目有些痛苦。亚马逊的Route53做的不错,但可选的自托管服务中,没有那么好的。
13.动态服务注册
比DNS更适用于高度动态的环境发现节点。
Zookeeper。被用于很多场景:配置管理、服务间的数据同步、leader选举、消息队列和命名服务。Zookeeper能确保数据在多个节点之间安全地复制,并且当节点故障后仍能保持一致性。
Consul。也支持配置管理和服务发现,其杀手级特性之一是提供了现成的DNS服务器。还具有对节点进行健康检查的能力。
Eureka。Netflix开源的系统,提供了基本的负载均衡功能,支持服务实例的基本轮训和调度查找。
构造你自己的系统。可以使用AWS的API来构造。
别忘了人。要有工具能在注册中心上显示报告和仪表盘给人看。
14.文档服务
要能生成服务的API文档。
Swagger。提供的终端用户体验不错,为超媒体核心中的增量探索概念做的很少。
HAL和HAL浏览器。Hypertext Application Language是一个描述公开的超媒体控制的标准。在使用超媒体的话,建议用HAL,没使用的话建议用Swagger。
15.自描述系统
Martin Fowler提出的人文注册表方法,有一个地方可以让人们记录组织中有关服务的信息,和维基一样简单。从活系统中抽取一些数据,形成静态Web页面或维基是一个好的开始。
16.小结
推荐Nygard的书《Release It!》,分享了关于系统故障的故事以及处理它们的模式,对于构建任何规模化系统都强烈推荐。