thinktt
作者thinktt2015-11-04 15:22
系统工程师, 罗云科技

我的《云计算核心技术剖析》读书笔记之本地测试服务器的设计、Datastore设计

字数 7878阅读 1205评论 0赞 0

本地测试服务器的设计

虽然App Engine本身的应用服务器的设计是不公开的,但是我们还是能从Python版的 App Engine SDK中一见端倪,因为其自带的本地测试服务器在设L十和代码方面都应该与实际 运行的应用服务器极为相似,这点也得到了来自开源App Engine产品AppScale的证明。由 于Java版的本地测试服务器有很多代码都是不可见的,所以本节将深入介绍Python版的本地 测试服务器的设计,并顺便对应用服务器的相关设计进行推测。下面首先讨论一下本地测试 服务器的架构。

一、架构

图3-9为本地测试服务器的架构图,主要包括4大组成部分:服务器Runtime、应用代码、 API接口和服务本地实现。下面将介绍除应用代码之外的其余3个部分。

12312.png

1. 服务器Runtime

这部分代码也是整个本地测试服务器最核心的代码,主要定义了服务器是如何启动的,将启动哪些服务和如何处理各种Web请求等。它们是构筑在Python Runtime之上的,并装载应用代码、API接口和服务本地实现。

2.  API接口

除了 Web请求之外,App Engine的其他功能基本上以API的形式发布给用户,并让用户来 调用。当一个API接口接收到请求时,它会将其转译为Protocol Buffer格式的请求,并且会依据其所处的环境来选择将清求发给哪个对应的实现。比如,如果在真实的应用服务器上,API 接口会把请求发给远程真实运行的服务,但如果它在测试服务器上运行,则会调用本地仅用于 测试的简单实现。

3.  服务本地实现

为了让测试服务器更好地模拟App Engine真实运行的情况,Google在测试服务器中将加 入服务本地实现来对代码发出的API请求进行有效处理。虽然处理的效果和实际的实现有可能 大相径庭,但是它返回给API请求的结果是一致的,这样能让开发人员有效地验证代码的作用 和正确性。

二、工作流程

接下来将介绍本地测试服务器在启动服务器、Web处理和服务调用这3方面的流程来帮助 大家理解其内部机制。

1.  启动服务器

输入 dev一appserver .py sample,调用 App Engine SDK 根目录下的 dev—app&erver.py 脚 本来启动测试服务器和桕关的名为sample的项目,之后这个脚本会调用H录google/appengine/ tools下的dev一appserver_main.py脚本来执行下面的步骤。

(1)  处理调用参数,比如上面这个命令中的sample。

(2)  分析sample这个项目中的app.yaml文件,并且初始化应用服务器自身的数据结构,比如Datastore所在的地址、日志的级别和HTTP服务的端口号等。

(3)  启动本地的管理界面。

(4)  启动所有服务的本地实现,包括Datastore、Blobstore、邮件发送、仟务队列等。

(5)  启动HTTP服务器,并监听相关的HTTP端14。

2.  Web处理

这块逻辑非常简单,当系统收到一个Web请求时,它会经历下面这几个步骤来处理并返回 相应的结果。

(1)  系统会根据这个Web请求的目标URL和在app.yaml上定义的URL与脚本之间的对应关系,来将这个请求转发给对应的脚本。

(2)     当脚本收到这个请求之后,它会根据这个请求的HTTP方法来调用这个脚本对应的方 法,比如假设这个请求的HTTP方法是get,那么会调用这个脚本里面的get方法。

(3)     这个方法会处理这个请求,并通过HTTP响应流来将结果发送给请求方。

3. 服务调用

就像上面提到的那样,App Engine fl带了很多服务,并且这些服务的调用机制某本一致。 下面将通过在前面例子中插入一个Blog对象来讲解调用Datastore这个服务的整个流程。

⑴生成一个基于Blog对象的实体,并调用其put方法。

(2)  检査这个实体是否已经存在子Datastore。如果已经存在,系统将更新它而不是重新创 建新的。

(3) 调用在目录 google/appengine/api 下 datastore.py 脚本中的 put 方法。

(4)  put方法首先将这个put请求转译为Protocol Buffer格式,并将请求发给本地的Datastore实现。要注意的是,如果服务器运行在真实环境下,它会将这个请求发给远程的BigTable服务, 让它来处理。

(5)  本地实现会依据设置的不同,将这个请求以Protocol Buffer的格式存储在一个本地文件 里,或者将这个请求的数据存储在一个小型的SQLite数据库电,并最终返回这个实体的键值以 表不插入成功。

Datastore 的设计

由于应用服务器无状态和易失的特性,它们在架构方面仍有简化的余地,但Datastore在数 据的持久性方面有硬性要求,同时也要能应对海量的访问请求,这使得其在设计上有非常大的 难度,被认为是AppEngine平台内部最难啃的“一块骨头”,而本节将试着给大家“啃”一下 这块“骨头”。在介绍如何设计和构建Datastore之前,将酋先从程序员的角度来介绍一下 Datastore在使用方面的一些信息。

一、使用方面

首先,在编程方而,Datastore是基干上面提到的“实体”(Entity)这个槪念的。而丑实体 比较类似于“对象”,同时实体可以包括多个厲性。属性的类别有整数、浮点数和字符串等。比如可以设计一个名为Person的实体,它包含名为Name的字符串属性和名为Age的整数属性。由于Datastore是“无范式”(Schema-less)的,所以数据的范式都由应用来维护,而且能非常 方便地对一个实体所包含的属性进行增删和修改。在存储方面,一个实体的实例可以被认为是 一个普通的“行”,而包含所有这种实体实例的表格被称为Kind,比如所有通过实体Person生成的实例(比如小吴、小朱和小华等)都会存放在同一个名为Person的Kind中。在结构方面,虽然也能通过特定的方式在Datastore中实现关系型结构,但是Datastore在设计上是为层 次性结构“坫身定做”的,有根实体和子实体之分。比如可以把Person作为根实体,Address 则作为Person的子实体,两者合在一起可以称为一个“实体组”。这样做的好处是能将这两个

实体集中一个BigTable本地分区中,而且能对这两个实体进行本地事务。

接下来,将谈一下Datastore支持的高级功能。其一是提供GQL这个查询g言。GQL是SQL 的一个非常小的子集,包含>、<和=等操作符。其二是App Engine会根据代码中的査询语句来自动生成相应索引,但不支持对复杂的综合索引的生成。艽三是虽然与传统的关系型数据库相 比,Datastore在速度方面有一定的差距,但是Google的架构师保证大部分对Datastore的操作 能控制在200ms之内,同时也得益于它的分布式设计,使得它在扩展性方面特别出色。其四是Datastore也支持在实体之间创建关系,比如在Python版的App Engine中可以使用引用属性在 实体间构建一对多或者多对多的关系。表3-8比较了 Datastore和传统的关系型数据库。


 

Datastore

关系型数据库

SQL支持

只支持一些基本査询

全部支持

主要结构

层次

关系

索引

部分可动创建

手动创违

亊务

只支持在一个实体组内执行

支持

平均执行速度

低于200ms

低干100ms

扩展型

非常好

很困难,而且需要进行大姓修改

最后,在接口方面,Python版提供一套私有的API和框架。在基本功能方面,比较容易学 习。但在部分高级功能方面,比如关系和事务等方面,学习难度很高。Java版的API是基于JD0 和JPA这两套官方的ORM标准,但是和事实上的标准Hibernate有一定的差异。

二、实现方面

在实现方面,Datastore是在BigTable的基础上构建的,所以本节会首先介绍一下BigTable, 然后会讲一下它在事务和备份这两方面所采用的机制。

1. BigTable

在本章开头,已经简要介绍了 BigTable技术,但BigTable艽实没有之前介绍的那样复杂, 它就是一个非常巨大的表格,这也是它之所以称为BigTable的原因。它的结构非常简单,就是 一行行的数据,每行都冇一个主键和多个列,但是为了支持海量数据,它将这个大的表格进行 了分片处理,这样每台服务器可以存储一个海量表格的一小部分。并且为了提髙査询效率,会 对这个表格进行排序。就像App Engine的创始人之一 Ryan Barrett所说的:“BigTable是一个经 过分片和排序的大数组。”

在功能方面,BigTable支持基本的CRUD操作,也就是增加(Create)、査询(Read),更 新(Update)和删除(Delete)操作,也支持对单行事务与基于前缀和范围的扫描。

Datastore主要包含两种类型的BigTable:其一是实体表,其二是索引。下面简要介绍一下 这两种类型。

•实体表

它是Datastore最核心的表格,是以BigTable的形式存在的,主要用于存储所有的实体。 它的格式非常简单,每行都会有一个键值[也称为实体键(EntityKey),可以认为它是一个实体 的主键],而且这个表只有唯一一个列,它主要用于存放被序列化的实体。

•索引

索引本身主要是为了方便和加速查询而生的,所以在切入索引之前,先介绍一下Datastore 主要支持哪些査询,主要有3类:其一是基于Kimi的,其二是基于单个属性值的,其三是基干 多个属性值的。

索引表也娃以BigTable的形式存在的,伹是和上面的实体表是分离的,主要用来单独存放 那些需要被索引的数据。而且由于怕索引表体积太大,所以有时为了提升査询速度,不会将其 放置在内存中。主要有下面这几种索引表。

□ Kind索引。加速那些用于获取所有属于某个Kind的实体的査询,比如査询所有属干 Person这个Kind的实体,那么小吴、小朱和小华等都会被提取出来。Kind索引表的每 行杳Kind的名称和实体键这两个列。此索引会由系统自动生成。

□单属性索引。用干加速那些基于单…属性值的査询,比如要找出所有年龄(Age)在20 岁之下的人,Age就是所谓的那个单一属性值。单属性索引表的每行除r Kind和实体 键之外,还有属性名和属性值这两个列。此索引也由系统自动生成,还会根据升降序的 不同,生成两个表。

□综合索引。用于加速那些基于多个属性值的査询。综合索引表和上面的单属性索引表非 常类似,佴是每行包括多个属性名和属性值,而且由于此索引消耗资源非常多,所以由 开发人自己来确定是不是需要这个索引,而不是由系统自动生成。

2. 事务

原则上,所有对单一实体的写操作都是事务性的,并且基于上面提到的BigTable的单ft事 务和乐观并发控制这两个技术。下面是流程。首先,系统会读这个实体的提交时间戳,写操作 会以串行的形式写入到BigTable的日志中。之后赛统会将日志更新到BigTable的表中,如果成 功的话,系统会更新这个实体的提交时间戳,但^果系统发现在更新之前,提交时间戳发生了 变化,也就是说在这个事务执行过程中,另一个事务已经对这个实体进行了操作,此时系统会 重新执行这个事务。由于在整个事务过程中采用乐观并发控制,而不是加锁,所以在吞吐量方 面表现不错。如果要对多个实体执行事务,那就需要将这几个实体设为一个实体组,也就意味 着将这几个实体放在同一台物理机上。在执行的时候,会以根实体的提交时间戳为准来对所有 参与事务的实体进行和上面差不多的事务操作。

3. 备份

与BigTable基于行级別的备份不同的是,Datastore是基于实体组级别的,而且采用Paxos 算法,所以Datastore的备份方法比纯BigTable的更安全。

总体而言,Datastore在设计理念上和传统的关系型数据库冇很大的不同,所以它在反应 

速度和数据的写方面不是最优的,但是现在Web应用以数据读为主,而且需要能通过简单的 扩展来支持海*数据,而这两点却是Datastore所擅长的,所以Datastore非常适合支撑Web 应用。

总结

在介绍了 App Engine的使用、架构和几个模块的实现之后,最后将总结一下App Engine 在使用方面的注意点、最佳实践和适用场景,还会谈一些我个人对App Engine的期望。

一、注意点

虽然App Engine在功能上的确有其强大之处,但它也有一些缺点,主要有下面这6点。

□执行速度偏慢。由于其分布式设计,所以在速度方面不是最优的,比如普通的Memcache 能在几毫秒内完成操作,而App Engine的Memcache则大槪需要50ms才能完成操作。

□私有API。它的大多数API都是私有的,特别是在其服务方面,虽然Google提供了很 不错的文档,但是在学习和移植性等方面,成本都偏高。

□有时会出现执行失败的情况。根据很多人的实际经验,App Engine会不定时地出现执 行失败的情况,特别是Oatastore和URLFetch这两部分。虽然Google已经将Datastorc 方面出现错误的几率从原先的0.4%降至现在的0.1%,但是失败的情况是很难完全避 免的。

□有时会宕机。虽然总体而言,宕机并不频繁,但是在2010年年初出现达136分钟 的故障导致部分用户的应用无法正常运行,其发生原因来自于其备份数据中心出现了 问题。

□无法选择合适的数据中心。比如,你的应用所面对的用户主要在欧洲,但是你的应用所 属的App Engine服务器却很有可能被部署在一个美国的数据中心内。虽然你的应用很 有可能在将来会被移动至欧洲某个数据中心,但是你却无法控制整个过程。

□有时处理请求会起时。虽然平均能在100ms至200ms之间完成海漿请求,但是有时会出 现处理请求超时的情况。

二、最佳实践

对于如何更好地使用App Engine,业界已经积累了一定的最佳实践,下面是其中最具代表 性的8个。

□适应App Engine的数据模型。因为其数据模型并不是传统的关系模型,而且在性能方 面的表现也和关系型数据库差別很大,所以如果想要用好非常关键的Datastore,那么理 解和适应其数据模型是不可或缺的。

□对应用进行切分。由子App Engine对每个应用都有一定的资源限制,所以为了让应用 更SOA化和更模块化,可以将一个应用切分为多个子应用。比如,可以分成一个用于 前端的Web应用和多个用于REST服务的后台应用。

口尽可能多地利用Memcache。这样不仅能减少昂贵的Datastore操作,而1能减轻 Datastore 的压力。

□在上面提到过,由于AppEnginc在执行某些操作时会出现失败的情况,比如Datastore 方面,所以要在设计和实现这两方面作好相应的异常处理工作。

□由于Datastore不是关系型数据库,在执行常见的求和操作时显得有点“捉襟见肘”,所 以最奸使用Google推荐的Sharded Counters技术来计算总数。

□由干Blobstore还只是刚走出试验期而已,而且其他模块对静态文件支持不佳,比如 Datastore只支持1MB以内的对象,每个应用最多只能上传1000个文件,而且速度不是 最优,所以推荐使用其他专业的云存储,比如Amazon的S3或者Google Storage等。

口尽量使用批处理方式,不论是在使用Datastore还是发送邮件等。

□不要手动创建索引,因为AppEngine会自动根据你在代码中的査询来创建相关的索引。

三、适用场景

基于现有相关的实践,主要有下面这3个场景能将App Engine的能力发挥得淋漓尽致。

□ Web Hosting。这是最常见的场景。在App Engine上已经部署了数以十万计的小型网站 (当然其中有很多主要是为了学习的),而且还部署了一些突发流量很大的网站,其中最 著名的例子就是美国白宵的“OpeiiForQuestions”这个站点,它主要用于给美国总统提 问的,这个站点在短短的几个小时内处理接近W万级别的流量。

□ REST服务。这也是在AppEngine平台上很常见的场景,最出名的例子就是BuddyPoke。 BuddyPoke的客户端就是一个Flash应用,在用户的浏览器上运行,而它的服务器端则 是以REST服务的形式放置在App Engine上的,每当Flash客户端需要读取和存储数据 的时候,它都会发请求给后端的REST服务,来让艽执行相关的Datastore操作。

□依赖Google服务的应用,比如,应用能够通过App Engine的E-mail服务来发送大规模 的电子邮件。

四、对未来的期望

对于App Engine,我个人希望它能越来越完善,功能越来越强大。下面是我的4点期望。

口更稳定的表现、更少的超时异常和更快的反应速度,特别是在Datastore和Memcached 这两方面。

□支持对数据中心的选择。虽然现在App Engine会根据应用的用户群的所在地来调整应 用所在的数据中心,倂由干整个过程对开发者而言是不可控的,所以希望在创建应用的 时候,能让用户自己选择合适的数椐中心。

□更好的SLA。如果App Engine能像S3那样设定一些SLA条款,那么将使用户更放心

地在App Engine 1:部署应用。

□新的语言支持,比如PHP。但是如果在现有的App Engine架构上添加一门新的语言, 整个工作S会非常大,因为App Engine有接近一半的模块是语言特定的,比如应用服 务器和开发环境等,所以短期内我认为不太可能支持新语言。

在Google大战略中,App Engine是其中不可分割的一部分,因为Google希望能通过App Engine这样的PaaS平台来降低Web应用开发的难度。只要难度降低了,Web应用替代客户端 应用的整体速度将会加快,那样将会对Google今后的发展非常有利。而对我们而言,App Engine 不仅是一个非常适合部署Web应用的平台,同时也是一个很不错的能让我们深入学习PaaS技 术的机会。下一章将关注业界另一款著名的PaaS服务Salesforce Force.com。 

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

0

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广