随着多元化微服务的流行,越来越多的服务开始采用微服务来构建。近日,曾在Google和eBay担任高级职务的Randy Shoup在博客中分享了其从这两个公司所学习到的构建大规模服务架构的经验。本文对Randy谈论的内容进行了总结,为那些没有创建、使用和保护大规模架构的工程师提供参考。
大规模系统和多元化微服务最终一定会有所牵连。其中,多元化的意思是多种语言共同编写。
创建于1995年的eBay共经历了3次架构变更
eBay最早采用的是创始人花费两天时间所编写的Perl程序。
之后,它开始使用由340万行C 代码所编写的程序。
然后,eBay使用Java编写的分布式系统。
现在的ebay仍然使用了大量Java,同时也使用了很多由多种代码编写的多元化微服务。
Twitter的架构变更过程类似
Amazon的架构变更同样类似
首先,它使用的是C 程序。
然后,采用了Java和Scala两种语言。
目前,Amazon也依靠多元化微服务。
拥有多元化微服务的大规模生态系统运行情况如何呢?
目前运行良好的系统都是不断变革的产物。例如,Google从来没有对系统进行过集中的设计。当前的系统纯粹是适应时代和技术发展演变而成的。
变异和自然选择。当一个新的问题出现,工程师通常选择利用已有的产品或服务来解决。因此,一个服务只有在不断的提供价值、不断被使用的情况下,才能避免被淘汰的命运。
这些大规模系统采用了自底向上的设计方法。
以Google App Enginer(GAE)中所使用的一些服务为例:
Cloud Datastore嵌套依赖于以下服务:Megastore(一种结构化数据库)→Bigtable(一种集群级的结构化服务)→Colossus(一种集群化文件系统)→Borg(集群管理框架)。
层次关系十分清楚。每一层都添加了下一层中不存在的一些东西。
它采用自底向上的方法设计而成。首先是构建Colossus文件系统最后才是Cloud Datastore。
标准化可以在没有集中控制的情况下完成
通信通常会被标准化为:
网络协议。Google采用Stubby协议,而eBay采用REST协议。
数据格式。Google采用Protocol Buffer,而eBay使用JSON。
接口语法标准。Google采用Protocol Buffer,而JSON由自己的语法。
框架中通常会被标准化的通用模块包括:
源码控制。
配置管理。
集群管理者。
监控系统。
警告系统。
诊断工具
所有能够脱离传统的组件。
在一个充满变革的环境,标准是强制执行的:编码、提交、代码审查和代码检索。
鼓励最佳实践的最好方式就是用实际的代码说话。这不是自顶向下的审查或者前卫的设计,而是某个人能够编写出让工作尽快完成的代码。
鼓励是通过团队提供库的方式进行的。
鼓励也通过工程师在支持X协议或Y协议时所依赖的服务进行。
Google在代码方面广为人知的是:任何一行代码在进行源码控制时,至少由一个额外的编码人员进行审查。这就是一种大家交流自己经验的良好方式。绝大部分情况下,Google的每一个工程师都可以检索整个代码库。在编程人员规划如何实施一个事情时,代码库可以提供很好的参考。在1万个工程师不停工作的情况下,一个编程人员想做的事情可能已经被其他人提前完成了。这就使得一个好的项目能够通过代码库进行传播。当然,代码中的错误也同样可能会传播。
为了鼓励通用项目和标准化的习惯,应使得做正确的事情简单,而做错误的事情要困难很多。
服务之间是相互独立的:
新服务只有在他们的使用被证实的时候才被创建。
通常,一种功能是为一种特别的使用场景而设计。然后,该功能被发现通用而且有效:
这些架构通过实用主义的方法进行发展。没有人可以命令某项服务应该被添加。
Google文件系统支持搜索引擎。分布式文件系统显然更容易变得通用。
Bigtable初始支持搜索引擎,但却没有被广泛使用。
Megastore设计的初衷是作为Google应用的存储机制,却被广泛使用。
GAE初始是由一群工程师为帮助设计网站而构建。
Gmail来源于Google内部经常使用的一个项目,然后才被推广到外部使用。
如果一个服务不再被使用会怎么样?
当服务拥有者在构建大规模多元化微服务的系统时,情况是什么样的呢?
在一个大规模框架中执行很好的服务应该是:
目的单一。它应该拥有一个定义良好的接口。
模块化和独立性。也就是所谓的微服务。
不应该共享一个持久层。
满足客户需求。在合适的质量水平下提供必要的功能,同时满足协议的性能、维护稳定性和可靠性以及不断的改善服务。
以最小的代价和努力满足需求。
谁构建谁运行。
作为一个有限上下文的服务。
即使是在同一公司内,不同服务间的关系也如同厂商和客户的关系。
尽量友好并合作,但是一定要慎重对待服务间的关系。
一定要清楚所属关系。
一定要明确每个人的分工。定义一个明确的接口,并维护好。
客户可以选择是否使用该服务是调整激励的有效手段。通过客户来鼓励服务朝着正确的方向发展。这也是新服务构建的方式之一。
定义SLA。因为服务提供商向客户提供了一定的保证,客户可以依赖该服务。
使用服务的团队需要支付服务费用:
向服务支付费用可以带来经济上的激励。它可以刺激提供商和客户有效利用资源。
当东西免费时,人们总是不懂得珍惜。
例如,一个内部客户曾免费使用GAE,并使用了大量资源。要求他们更加高效的使用资源被证实并不管用。但是,在引入收费机制一周后,他们很快就能够通过1-2处的简单修改,减少GAE 90%的资源消耗。
并非使用GAE的团队不懂得珍惜。他们有自己更应该关注的方面,因此优化GAE的使用并不在他们的目标列表中。然而,事实证明,更加高效的架构能够带来更好的反应时间。
付费也能够刺激服务提供商来保证服务质量。否则,一个内部的客户也可能转而使用其他服务。付费直接带来好的开发和管理项目。代码审查就是一个例子。Google的超大规模设计和测试系统是另外一个例子。Google每天都会运行百万次的自动化测试。一旦代码被版本库接收,所有相关代码的接收测试就会被运行。这种方法可以很好的帮助小团队维护服务质量。
作为一个服务提供商,操作多元化微服务的大规模系统中的服务是怎样的呢?
可预测的性能是基本要求。
大规模服务很容易出现性能上的变化。
性能的可预测性要远比平均性能重要的多。
无法保证性能的低延迟根本就不能算是低延迟。
当服务提供可预测的性能时,客户更容易针对服务进行编程。
当一个服务使用很多其他的服务来完成相关工作时,延迟最大的服务决定了该服务的性能。
以平均延迟为1ms、最大延迟以0.001%的概率为1s的服务为例:
调用一次该服务就意味着0.01%概率时间延后。
那么,对于一个Google所采用的包含5000台机器的大规模服务而言,延后时间概率就为50%。
例如,内存缓存相关的问题会以百万分之一的概率被追踪到一个底层数据结构的重新分配事件。但延迟出现大的抖动时,这个问题就会浮现到较高的层。然而,底层的细节对于大规模系统才十分重要。
深度的可靠性。
服务中断一般都是人为操作引起,而非硬件或软件错误。
一定要可以应对机器、集群和数据中心的失效。
当使用其他服务时,要实现负载均衡和流量控制
支持快速回滚操作。
增量部署。
不要一次就部署到所有的机器。选择一个系统,把新版本的软件部署上去,然后观察系统工作情况如何。
如果工作情况良好,就把部署的机器规模扩张到10%,然后20%,最后才是所有的机器。
如果部署到50%时出现问题,要能够进行回滚操作。
eBay利用特征选项来解耦合代码部署和特征部署。通常情况下,首先关闭特征,然后部署代码,最后再选择打开或关闭特征。这使得代码可以在新的特征打开之前被正确部署。同时,这也意味着,如果新的特征存在bug、性能问题或者业务问题,特征可以在不部署新代码的情况下被关闭。
警告可能会多,但监控却永远不会多。
超级服务:
共享的持久化:
在分层模型中,服务放置在应用层,而持久化层则是一个提供给应用的通用服务。
eBay也采用了同样的方法,但是不管用。它破坏了服务的封装。应用程序可以通过升级数据库“黑”进服务。它最终会重新引进多个服务。共享数据库不允许松耦合服务。
微服务通过变得小、隔离和独立来预防该问题,也通过这种方式来保证生态系统健康成长。
|