当前位置:首页 > 工程案例 > 正文

道路监控项目

  根据《中国高被引图书年报》,刘鹏教授所著《云计算》被引用量,名列中国所有自动化技术和计算机技术图书第一名。

  2.6 大规模分布式系统的监控基础架构Dapper

  Google认为系统出现故障是一种常态,基于这种设计理念,Google的工程师们结合Google的实际开发出了Dapper。这是目前所知的第一种公开其实现的大规模分布式系统的监控基础架构。

  2.6.1 基本设计目标

  Google使用最多的服务就是它的搜索引擎,以此为例,有资料表明,用户的平均每一次前台搜索会导致Google的后台发生1011次的处理。用户将一个关键字通过Google的输入框传到Google的后台,系统再将具体的查询任务分配到很多子系统中,这些子系统有些是用来处理涉及关键字的广告,有些是用来处理图像、视频等搜索的,最后所有这些子系统的搜索结果被汇总在一起返回给用户。在道路监控系统施工方案我们看来很简单的一次搜索实际上涉及了众多Google后台子系统,这些子系统的运行状态都需要进行监控,而且随着时间的推移Google的服务越来越多,新的子系统也在不断被加入,因此在设计时需要考虑到的第一个问题就是设计出的监控系统应当能够对尽可能多的Google服务进行监控,即广泛可部署性(Ubiquitous Deployment)。另一方面,Google的服务是全天候的,如果不能对Google 的后台同样进行全天候的监控很可能会错过某些无法再现的关键性故障,因此需要进行不间断的监控。这两个基本要求导致了以下三个基本设计目标。

  (1)低开销道路监控系统施工方案:这个是广泛可部署性的必然要求。监控系统的开销越低,对于原系统的影响就越小,系统的开发人员也就越愿意接受这个监控系统。

  (2)对应用层透明道路监控系统施工方案:监控系统对程序员应当是不可见的。如果监控系统的使用需要程序开发人员对其底层的一些细节进行调整才能正常工作的话,这个监控系统肯定不是一个完善的监控系统。

  (3)可扩展性:Google的服务增长速度是惊人的,设计出的系统至少在未来几年里要能够满足Google服务和集群的需求。

  2.6.2 Dapper监控系统简介

基本概念

  对系统行为进行监控的过程非常的复杂,特别是在分布式系统中。为了理解这种复杂性,首先来看如图2-30[18]所示的一个过程。

  在图中,用户发出一个请求X,它期待得到系统对它做出的应答X。但是接收到该请求的前端A发现该请求的处理需要涉及服务器B和服务器C,因此A又向B和C发出两个RPC(远程过程调用)。B收到后立刻做出响应,但是C在接到后发现它还需要调用服务器D和E才能完成请求X,因此C对D和E分别发出了RPC,D和E接到后分别做出了应答,收到D和E的应答之后C才向A做出响应,在接收到B和C的应答之后A才对用户请求X做出一个应答X。 在监控系统中记录下所有这些消息不难,如何将这些消息记录同特定的请求(本例中的X)关联起来才是分布式监控系统设计中需要解决的关键性问题之一。

图2-30 典型分布式系统的请求及应答过程

  一般来说,有两种方案可供选择:黑盒(Black Box)方案及基于注释的监控(Annotation-based Monitoring)方案。二者比较而言,黑盒方案比较轻便,但是在消息关系判断的过程中,黑盒方案主要是利用一些统计学的知识来进行推断,有时不是很准确。基于注释的方案利用应用程序或中间件给每条记录赋予一个全局性的标示符,借此将相关消息串联起来。考虑到实际的需求,Google的工程师最终选择了基于注释的方案,为了尽可能消除监控系统的应用程序对被监控系统的性能产生的不良影响,Google的工程师设计并实现了一套轻量级的核心功能库,这将在后面进行介绍。

  Dapper监控系统中有三个基本概念:监控树(Trace Tree)、区间(Span)和注释(Annotation)。如图2-31[18]所示是一个典型的监控树,从中可以看到所谓的监控树实际上就是一个同特定事件相关的所有消息,只不过这些消息是按照一定的规律以树的形式组织起来。树中的每一个节点称为一个区间,区间实际上就是一条记录,所有这些记录联系在一起就构成了对某个事件的完整监控。从图2-31不难看出,每个区间包括以下内容:区间名(Span Name)、区间id(Span id)、父id(Parent id)和监控id(Trace id)。区间名主要是为了方便人们记忆和理解,因此要求这个区间名是人们可以读懂的。区间id是为了在一棵监控树中区分不同的区间。父id是区间中非常重要的一个内容,正是通过父id才能够对树中不同区间的关系进行重建,没有父id的区间称为根区间(Root Span)。图2-31中的Frontend Request就是一个根区间。在图中还能看出,区间的长度实际上包括了区间的开始及结束时间信息。

  监控id在图2-31中并没有列出,一棵监控树中所有区间的监控id是相同的,这个监控id是随机分配的,且在整个Dapper监控系统中是唯一的。正如区间id是用来在某个监控树中区分不同的区间一样,监控id是用来在整个Dapper监控系统中区分不同的监控。注释主要用来辅助推断区间关系,也可以包含一些自定义的内容。图2-32[18]展示了图2-31中区间Helper.Call的详细信息。

  

  图2-31 监控树

  在图2-32中可以清楚地看到这个区间的区间名是“Helper.Call”,监控id是100,区间id是5,父id是3。一个区间既可以只有一台主机的信息,也可以包含来源于多个主机的信息;事实上,每个RPC区间都包含来自客户端(Client)和服务器端(Server)的注释,这使得双主机区间(Two-host Span)成为最常见的一种。图2-32中的区间就包含了来自客户端的注释信息:“”、“Client Send”、“Client Recv”和“”,也包含了来自服务器端的注释信息:“Server Recv”、“foo”和“Server Send”。除了“foo”是用户自定义的注释外,其道路监控系统施工方案他的注释信息都是和时间相关的信息。Dapper不但支持用户进行简单的文本方式的注释,还支持键—值对方式的注释,这赋予了开发者更多的自由。

图2-32 区间Helper.Call的详细信息

  2.监控信息的汇总

  Dapper对几乎所有的Google后台服务器进行监控。海量的消息记录必须通过一定的方式汇集在一起才能产生有效的监控信息。在实际中,Dapper监控信息的汇总需要经过三个步骤,如图2-33[18]所示。

  (1)将区间的数据被写入到本地的日志文件。

  (2)利用Dapper守护进程(Dapper daemon)和Dapper收集器(Dapper Collectors)将所有机器上的本地日志文件汇集在一起。

  (3)将汇集后的数据写入到Bigtable存储库中。

  从图中也很容易地看出,(1)和(2)是一个读的过程,而(3)是一个写的过程。选择Bigtable主要是因为区间的数目非常多,而且各个区间的长度变化很大,Bigtable对于这种很松散的表结构能够很好地进行支持。写入数据后的Bigtable中,单独的一行表示一个记录,而一列则相当于一个区间。这些监控数据的汇总是单独进行的,而不是伴随系统对用户的应答一起返回的。如此选择主要有如下的两个原因:首先,一个内置的汇总方案(监控数据随RPC应答头返回)会影响网络动态。一般来说,RPC应答数据规模比较的小,通常不超过10KB。而区间数据往往非常的庞大,如果将二者放在一起传输,会使这些RPC应答数据相对“矮化”进而影响后期的分析。另一方面,内置的汇总方案需要保证所有的RPC都是完全嵌套的,但有许多的中间件系统在其所有的后台返回最终结果之前就对调用者返回结果,这样有些监控信息就无法被收集。基于这两个考虑,Google选择将监控数据和应答信息分开传输。

图2-33 监控信息的汇总

  安全问题是所有系统都必须考虑的问题,为了防止未授权用户对于RPC信息的访问,信息汇总过程中Dapper只存储RPC方法的名称却不存储任何RPC负载数据,取而代之的是,应用层注释提供了一种方便的选择机制(Opt-in Mechanism):应用程序开发者可以将任何对后期分析有益的数据和区间关联起来。

  2.6.3 关键性技术

  前面提到了Dapper的三个基本设计目标,在这三个目标中,实现难度最大的是对应用层透明。为了达到既定设计目标,Google不断进行创新,最终采用了一些关键性技术解决了存在的问题。这些关键性技术概括起来主要包括以下两个方面。

轻量级的核心功能库

  这主要是为了实现对应用层透明,设计人员通过将Dapper的核心监控实现限制在一个由通用线程(Ubiquitous Threading)、控制流(Control Flow)和RPC代码库(RPC Library Code)组成的小规模库基础上实现了这个目标。其中最关键的代码基础是基本RPC、线程和控制流函数库的实现,主要功能是实现区间创建、抽样和在本地磁盘上记录日志。用C++的话Dapper核心功能的实现不超过1000行代码,而用Java则不到800行。键/值对方式注释功能的实现需要额外增加500行代码。将复杂的功能实现限制在一个轻量级的核心功能库中保证了Dapper的监控过程基本对应用层透明。

  2.二次抽样技术

  监控开销的大小直接决定Dapper的成败,为了尽可能地减小开销,进而将Dapper广泛部署在Google中,设计人员设计了一种非常巧妙的二次抽样方案。二次抽样顾名思义包括两次抽样过程。Google每天需要处理的请求量惊人,如果对所有的请求都进行监控的话所产生的监控数据将会十分的庞大,也不利于数据分析,因此Dapper对这些请求进行了抽样,只有被抽中的请求才会被监控。在实践中,Dapper的设计人员发现了一个非常有意思的现象,那就是当抽样率低至1/1024时也能够产生足够多的有效监控数据,即在1024个请求中抽取1个进行监控也是可行的,这种低抽样率有效的原因在于巨大的事件数量使关注的事件可能出现的足够多,从而可以捕获有效数据。这就是Dapper的第一次抽样。最初Dapper设计的是统一的抽样率,但是慢慢地发现对于一些流量较低的服务,低抽样率很可能会导致一些关键性事件被忽略,因此Dapper的设计团队正在设计一种具有适应性抽样率的方案。尽管采取抽样监控,所产生的数据量也是惊人的。根据Dapper团队的统计,Dapper每天得到的监控数据量已经超过1T,如果将这些数据全部写入Bigtable中效率较低,而且Bigtable的数据存储量有限,必须定期处理,较少的数据能够保存更长的时间。对此,Dapper团队设计了第二次的抽样。这次抽样发生在数据写入Bigtable之前,具体方法是将监控id散列成一个标量z,其中0≤z≤1。如果某个区间的z小于事先定义好的汇总抽样系数(Collection Sampling Coeficient),则保留这个区间并将它写入Bigtable。否则丢弃不用。也就是说在采样决策中利用z值来决定某个监控树是整棵予以保留还是整棵弃用。这种方法非常的巧妙,因为在必要时只需改动z值就可以改变整个系统的写入数据量。利用二次抽样技术成功地解决了低开销及广泛可部署性的问题。

  上面的两种技术手段解决了主要设计问题,这使得Dapper在Google内部得到了广泛的应用。Dapper守护进程已成为Google镜像的一部分,因此Google所有的服务器上都有运行Dapper。

  2.6.4 常用Dapper工具

Dapper存储API

  Dapper的“存储API”简称为 DAPI,提供了对分散在区域Dapper存储库(DEPOTS)的监控记录的直接访问。一般来说,有以下三种方式可以对这些记录进行访问。

  (1)通过监控id访问(Access by Trace id):利用全局唯一的监控id直接访问所需的监控数据。

  (2)块访问(Bulk Access):DAPI可以借助MapReduce来提供对数以十亿计的Dapper监控数据的并行访问。用户覆写一个将Dapper监控作为其唯一参数的虚函数(Virtual Function),在每次获取用户定义的时间窗口内的监控数据时架构都将引用该函数。

  (3)索引访问(Indexed Access):Dapper存储库支持单索引(Single Index),因为监控id的分配是伪随机的,这是快速访问同特定服务或主机相关监控的最好方式。

  根据不完全的统计,目前大约有三个基于DAPI的持久应用程序,八个额外的基于DAPI的按需分析工具及15~20个使用DAPI框架构建的一次性分析工具。

  2.Dapper用户界面

  大部分的用户在使用Dapper时都是通过基于Web的交互式用户界面,图2-34~图2-38显示其一般性的使用流程。

  (1)首先用户需要选择监控对象,包括监控的起止时间、区分监控模式的信息(图2-34[18]中是区间名)及一个衡量开销的标准(图2-34中是服务延迟)。

  (2)如图2-35[18]所示,一个大的性能表给出了所有同指定监控对象有关的分布式执行模式的简要情况。用户可以按其意愿对这些执行模式进行排序并选择某一个查看更多的细节。

  (3)图2-36[18]是某个选中的分布式执行模式,该执行模式以图形化描述呈现给用户。

图2-35 监控对象相关的执行模型

  

图2-36 特定的执行模式

  (4)根据最初选择的开销度量标准,Dapper会以频度直方图的形式将步骤(3)中选中的执行模式的开销分布展示出来,如图2-37[18]所示,同时呈现给用户的还有一系列特殊的监控样例信息,这些信息落在直方图的不同部分。用户可以进一步的选择这些监控样例。

  

图2-37 执行模式开销的频度直方图

  (5)在用户选择了某个监控样例后,就会进入所谓的监控审查视图(Trace Inspection View)。图2-38[18]是部分的监控审查视图,在这个视图中,最顶端是一条全局的时间线(Global Time Line)。每一行是一个监控树,选择“+”或“-”能够展开或折叠监控树。每个监控树用嵌套的彩色长方形表示的。每个RPC区间又被进一步的分成花在服务器处理上的时间和花在网络通信上的时间。用户注释并未在图中显示出来,但是它们可以按照逐个区间被选择包含在全局时间线上。

  

图2-38 监控审查视图

  根据统计,一个普通的工作日内大概有200个不同的Google工程师在使用Dapper用户界面。因此,在一周的时间里,有750~1000个不同的用户。

  2.6.5 Dapper使用经验

  本节介绍Dapper在Google中的一些使用经验,通过这些经验可以看出在哪些场景中Dapper是最适用的。

新服务部署中Dapper的使用

  Google的AdWords系统的构建围绕着一个由关键字命中准则和相关的文字广告组成的大型数据库。在这个系统进行重新开发时,开发团队从原型系统直到最终版本的发布过程中,反复的使用了Dapper。开发团队利用Dapper对系统的延迟情况进行一系列的跟踪,进而发现存在的问题,最终证明Dapper对于AdWords系统的开发起到了至关重要的作用。

  2.定位长尾延迟(Addressing Long Tail Latency)

  Google最重要的产品就是搜索引擎,由于规模庞大,对其进行调试是非常复杂的。当用户请求的延迟过长,即延迟时间处于延迟分布的长尾时,即使最有经验的工程师对这种端到端性能表现不好的根本原因也常常判断错误。通过图2-39[18]不难发现,端到端性能和关键路径上的网络延迟有着极大的关系,因此发现关键路径上的网络延迟常常就能够发现端到端性能表现不佳的原因。利用Dapper恰恰能够比较准确的发现关键路径。

图2-39 关键路径网络延迟对于端到端性能表现的影响

  3.推断服务间的依存关系(Inferring Service Dependencies)

  Google的后台服务之间经常需要互相的调用,当出现问题时需要确定该时刻哪些服务是相互依存的,因为这样有利于发现导致问题的真正原因。Google的“服务依存关系”项目使用监控注释和DPAI的MapReduce接口实现了服务依存关系确定的自动化。

  4.确定不同服务的网络使用情况

  在Dapper出现之前,Google的网管人员在网络出现故障时几乎没有工具能够确定到底是哪个部分的网络出现的故障。而现在Google利用Dapper平台构建了一个连续不断更新的控制台,用来显示内部集群网络通信中最活跃的应用层终端。这样在出现问题时可以最快的定位占用网络资源最多的几个服务。

  5.分层的共享式存储系统

  Google中的许多存储系统都是由多个相对独立且具有复杂层次的分布式基础架构组成。例如,Google App Engine是构建在一个可扩展的实体存储系统之上的。而该实体存储系统则是构建在底层的Bigtable之上,展现出一些RDBMS(关系型数据库管理系统)的功能。而Bigtable又依次用到了Chubby和GFS。在这样的层次式系统中决定端用户的资源消耗模式并不总是那么简单。例如,由Bigtable的单元引起的GFS高流量可能主要由一个用户或几个用户产生,但是在GFS的层次上这两种不同的使用模式是没法分开的。更进一步,在没有Dapper之类工具的情况下对于这种共享式服务资源的争用也同样难以调试。

  6.利用Dapper进行“火拼”(Firefighting with Dapper)

  这里所谓的“火拼”是指处于危险状态的分布式系统的代表性活动。正在“火拼”中的Dapper用户需要访问最新的数据却没有时间来编写新的DAPI代码或者等待周期性的报告,此时可以通过和Dapper守护进程的直接通信,将所需的最新数据汇总在一起。

  Dapper在Google内部取得了巨大的成功,虽然这种成功在一定程度上得益于Google内部系统的同构性,但是Dapper团队的创新性设计才是系统取得成功的根本性因素。Google的后台系统可以说是目前全球最大的一个云平台,读者借鉴Dapper的设计思想一定能够为不同规模的云平台设计出合适的监控系统。

  精彩回顾:

  《云计算(第三版)》精华连载(10)《云计算(第三版)》精华连载(9)《云计算(第三版)》精华连载(8)《云计算(第三版)》精华连载(7)《云计算(第三版)》精华连载(6)

  点击下方“阅读原文”了解《云计算(第三版)》

  ↓↓↓

相关文章:

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。