莎士比亚搜索:一个示范服务
为了更好地说明一个服务是怎样利用各种基础设施,以及是如何在Google生产环境中部署的,我们在这里提供一个假想的莎士比亚搜索服务。这个服务的作用是在所有莎士比亚的文献中搜索给定的词语。
整个系统可以分为两大部分:
● 批处理部分(batch)。给全部莎士比亚文献创建索引,同时将索引写入一个Bigtable中。这项任务只需运行一次(如果发现了新的莎士比亚文献,那就需要再运行一次。)
● 一个应用程序前端服务器(frontend),用以接收处理用户请求。该服务器是一直运行的,因为全球范围的用户都需要使用我们的服务。
批处理部分可以用MapReduce框架完成,三个阶段的实现分别如下所示。
● Mapping 阶段:该程序遍历所有的莎士比亚的文字,将其分成具体的单词。这项任务可以利用多实例并行加速。
● Shuffle 阶段:该程序将上一阶段产生的所有单词和位置等进行排序。
● Reduce 阶段:将上一阶段产生的单词或位置等按单词合并,产生一个新的单词或位置列表。
最后,程序将每一个单词或位置列表写入Bigtable 中,Row Key就是这个单词。
用户请求的处理过程
图2-4 显示了一个用户请求的处理全过程。首先,用户使用浏览器访问https://shakespeare.google.com。为了获得IP地址,用户设备需要请求DNS服务器(1)。该DNS请求最后会到达Google的DNS服务器。Google的DNS 服务器会请求GSLB系统。GSLB 通过全局流量负载信息,决定使用哪个IP地址回复用户。
图2-4:用户请求处理过程
用户浏览器利用获得的IP地址连接到HTTP服务器,这个服务器(Google前端服务器GFE)负责终结TCP 连接,并且反向代理请求到真正的服务器上(2)。GFE 从配置文件中找到该请求对应的后端服务(配置文件中包括所有的Google服务,例如Web Search、maps以及本例中的 Shakespeare)。GFE再次咨询GSLB系统,获得一个GSLB分配的、目前可用的Shakespeare服务器地址,向其发送一个RPC请求(3)。
Shakespeare前端服务器分析接收到的请求,构建出一个具体查询的Protobuf 请求。这时Shakespeare前端服务器需要联系后端服务器来做具体查询。前端服务器也需要联系GSLB 服务,获取目前可用的(同时符合负载均衡条件的)后端服务器的BNS地址(4)。Shakespeare 后端服务器随后请求Bigtable 服务器来获得最终查询结果(5)。
最终结果被写入一个Protobuf 结构体中,返回给Shakespeare后端服务器,后端服务器将其回复给Shakespeare前端服务器,前端服务器最终根据这个数据结构构建HTML回复给最终用户。
上述这些连锁事件其实一共耗时几百毫秒。因为一个请求涉及很多组件,这些组件都必须相当可靠才行,GSLB 服务如果出现问题将会造成严重故障。但是Google 依靠严格的测试和灰度发布流程,以及很多主动优雅降级的措施,使得我们可以为用户提供一个非常稳定的服务。由于Google的可靠性举世闻名,人们经常通过访问www.google.com来验证他们的网络服务是否正常。
任务和数据的组织方式
假设压力测试的结果显示,我们的服务器可以每秒处理大概100个请求(100 QPS)。通过对用户进行的调查显示,我们预计峰值流量会达到 3470 QPS,为了处理这些流量,至少需要 35个任务实例。但是,由于以下几点考量,我们最终决定至少采用37个实例,也就是 N+2模式:
● 在更新过程中,有一个任务实例将会短暂不可用,只有36个实例可提供服务。
● 如果另外一个物理服务器同时也出现问题,那么另外一个任务实例也受到影响,只剩35个实例可以对外服务,刚好可以满足峰值要求。[8]
假设,对用户流量的进一步观察显示我们的峰值流量其实是来自全球的。北美洲1430 QPS,南美洲 290 QPS,欧洲和非洲 1400 QPS,亚洲及澳大利亚共350 QPS。为了更好地服务用户,我们需要将服务分别部署在美国、南美洲、欧洲和亚洲。在南美洲,我们选择使用只部署4个实例(而不是5个),将冗余度降低为N+1。这样做的原因是,我们选择在极端情况下牺牲一些用户体验以降低成本。因为当容量不足时,GSLB会将南美洲的用户流量导向其他可用的数据中心,可以节省大概20%的硬件资源。在有条件的地方,我们还会将任务实例分散在不同的集群中,以便更好地提升可靠性。
因为Shakespeare后端服务器需要连接Bigtable服务读取数据,我们同时也需要合理地安排数据存储。亚洲的后端服务器尝试访问部署在美国的Bigtable会面临延迟问题。所以我们在每个地理区域都存放了Bigtable的副本。
利用Bigtable的复制功能,我们可以同时达到两个目的:
● 当Bigtable服务出现问题时,可以利用副本提供服务。
● 任务实例可以利用本地Bigtable 加速数据访问。
虽然Bigtable仅仅提供最终一致性保障(eventual consistency),但是由于数据更新并不频繁,所以对我们来说这并不是问题。
我们在这一章中介绍了很多术语,在接下来的章节中还会重复引用它们,所以读者并不一定现在就将它们完全记住。
[2]一个Google内部模拟灾难恢复的演习项目,参见第28章的“故障处理分角色演示”一节。
[3]第31章的“沟通:生产会议”一节会详细讨论这是如何发生的。
[4]大部分情况下,Google数据中心的硬件配置是统一的,但是也有例外情况,例如有些数据中心可能会同时存在几种不同迭代周期产生的硬件产品,我们有时候也会修改某个现有数据中心的硬件配置。
[7]Protocol Buffer是编程语言中性的、运行平台中性的一种数据序列化机制。更详细的内容可参见https://developer.google.com/protocol-buffers/。
[8]我们假设同时出现两个任务实例不可用的情况的可能性很低,足以忽略不计。但是单点故障源,例如供电设施问题,或者机柜交换机问题,可能会影响这里的假设。