天天看点

大规模服务设计部署经验谈(2) | 整体服务设计(2.6)

2.6               设计运营友好的服务的实践

设计运营友好的服务更具体的最佳实践包括:

2.6.1             快速服务健康测试。

这是构建验证测试的服务版本。这是一个嗅探型测试,可以快速在开发者的系统上运行,以保证服务不会以独立方式出错。要保证所有的边界条件都被测试到,是不可能的,但如果快速健康测试通过的话,那么代码就可以检入了。

2.6.2             在完整的环境中开发。

开发人员不但应当对他们的组件进行单元测试,而且还要对出现组件变更的整个服务进行测试。要高效实现这个目标,必须得有单服务器的部署,以及前一条最佳实践——快速的服务健康测试。

2.6.3             对下层组件的零信任。

设想下层组件会出现故障,并且确保组件会有能力恢复并继续提供服务。恢复的技巧和服务相关,但常见的技巧包括:

l      在只读模式中依靠缓存的数据继续运转;

l      在服务访问故障组件的冗余拷贝的短暂时间内,继续向用户群一小部分的所有人提供服务。

2.6.4             不要把同一个功能构建在多个组件中。

预见未来的交互是一件极其困难的事情,如果不慎引入冗余的代码,那么将来就不得不在系统的多处做修复。服务的增长和发展是很快的,稍不留神代码的质量就会很快恶化。

2.6.5             不同的集群之间不能互相影响。

大多数服务由小型集合或者系统的子集群组成,它们之间相互协作,共同提供服务,其中每个小型集合都可以相对独立地运作。每个小型集合应达到接近100%的独立程度,且不会有跨群的故障。全局服务,甚至包括冗余,是故障的中心点。有时候,这样的问题是不可避免的,但还是要设法保证每个集群都拥有各自所需的资源。

2.6.6             允许(少量)在紧急情况的人工干预

见场景是在灾难性事件或者其他紧急情况下移动用户数据。把系统设计成完全无须人工交互,但也要清楚小概率事件可能会发生,其中组合的故障或者未预期的故障都会需要人工交互。这些事件是会发生的,而在这些情况下,操作员的错误往往是导致灾难性数据丢失的常见来源。一名在半夜2点顶压工作的运营工程师可能会犯错误。将系统设计成一开始在多数情况下无须运营干预,但请和运营团队协作制定在他们需要进行干预时的恢复计划。比起将这些计划写进文档,变成多步骤易出错的过程,更好的办法是把这些规则写成脚本,并在生产环境中进行测试,以确保它们正常工作。没有经过产品环境试验的代码是不可行的,因此运营团队应当定时指挥使用这些工具进行“防火演习”。如果演习的服务可用性风险非常高,那么可以认为之前在工具的设计、开发和测试上的投资不足。

2.6.7             保持一切简单健壮。

复杂的算法和组件交互会给调试和部署带来成倍困难。简单到近乎傻瓜式的结构在大规模服务中几乎总是更胜一筹,因为在复杂的优化工作交付之前,交互中故障模式的数量早就足以磨灭人们的信心。通常我们的惯例是,能够带来一个数量级以上改善的优化工作才值得考虑,而只有百分之几或者甚至于只是低系数级别的提升,就不值得了。

2.6.8             全面推进准入控制。

所有良好的系统会在设计时开门见山地引入许可控制,这样符合一条长期以来为人们所认可的原则,那就是避免将更多的工作引入一个已经过载的系统,要比持续接受工作然后开始翻来覆去地检查好一些。在服务入口引入某些形式的节流或者准入控制是很常见的做法,但在所有的主要组件边界上都应该有准入控制。工作性质的变更最终会导致子组件的过载,即使整体服务仍然运行在可接受的负载级别。总体的惯例就是尝试采用优雅降级的方式,而不用在统一给所有用户低质量服务之前进行硬停机并阻断服务的入口。

2.6.9             给服务分区。

分区应当可以无限调整,并且高度细粒度化,并且不受任何现实实体(人、集合等)的限制。如果按公司分区,那么对于大的公司,就有可能超过单个分区的规模;而如

果按名称前缀进行分区,那么例如所有以P打头的最终一台服务器就可能会装不下。我们推荐在中间层使用一张查询表,将细粒度的实体,通常是在设计大规模服务的目标和应用服务提供商或IT外包商的目标之间,要划一道清楚的界限。应用服务提供商和IT 外包商往往人员更加密集,并且更乐于运行面向客户的复杂配置。

用户,映射到其数据相应被管理的系统上。这些细粒度的分区随后就可以自由在服务器之间移动。

2.6.10         理解网络的设计。

提早进行测试,了解机柜内的服务器之间、跨柜的服务器之间以及跨数据中心之间有哪些负载。应用程序开发人员必须理解网络的设计,且设计应当尽早交给来自运营团队的网络专员审核。