作者文章: Fenng

Get Architecture Done -《分布式Java应用:基础与实践》

按:承蒙林昊( @Bluedavy )看得起,嘱托我为他的大作《分布式Java应用:基础与实践》写序,倍感荣幸之余也颇有压力。读完本书的绝大部分章节后,这相信这会是我今年要向朋友们推荐的关于架构的图书。毕竟我在阿里系工作有年,对几家子公司的技术还算有所了解,内容有没有料还是可以一目了然的分辨的出来的。下文是推荐序。


  

提起诸如”高性能”、”高可用”、”大规模并发”、”可扩展性”这些词汇,我相信多数技术人的心情都是激动而稍有点复杂的,当然,也或许是不屑一顾。毕竟不是谁都有机会面对这些富有挑战的技术场景,也不是每个架构师在面对这些挑战之前都能做好技术上的准备。那些意外故障总是不期而至,疲于奔命的解决问题的场景回顾起来对架构师来说犹如一场噩梦。

  

本书阐述当一个面向数以亿计用户的网站经过几年高速发展,技术团队不得不面临大规模、高并发、高扩展性等挑战带来的技术困境的时候,一个出色的架构师经过多年一线实践后累积的经过时间考验的解决方案以及宝贵的实战经验。在这本书里,你会看到作者在解决一些关乎Web应用问题的指导原则、实践方法、多重工具的综合运用以及作者本人的感悟。要强调的是,本书讲述的内容是一个Web应用从小到大过程中遇到的棘手问题的解决之道,并非宏观解析,亦非屠龙之技。无论您面对的站点是大是小,皆会有参考作用,毕竟大站点会越来越复杂,而小站点总有一天也将变大。

  

如今到计算机书店里走一下,会发现Java架构相关的技术图书业已不少,但仍有理由相信本书内容填补了在Java架构实战方面的空白。在互联网应用大行其道的今天,有些名义上主题为Java架构的图书,要么单从Java本身阐述,缺乏整体应用的大局观;要么是高屋建瓴,从编程思想的高度坐而论道,缺乏实践性;要么是闭门造车之作,缺乏验证性。本书作者林昊多年来致力于推动OSGi在国内的发展,不乏理论技术功底,而后加盟淘宝网 (Taobao.com)的几年间奋战在架构一线,爬摸滚打积累了丰富的实践心得。所以,本书是一本不折不扣的”理论结合实践“之作。

  

考虑国内的技术图书出版环境以及必须尽力迎合读者的预期,写书本身是一件费力不讨好的事情,但将知识传递给更多人无疑是让人快乐的。现在,经过作者近两年的梳理与总结,这本书即将出版,相信您在研读本书之后有所收获并运用到您所面对的Web应用上,也期待将来有更多朋友能够分享架构实践经验,不亦快哉!

EOF
  

探索Google App Engine背后的奥秘(6)- 总结

按:此为客座博文系列。投稿人吴朱华曾在IBM中国研究院从事与云计算相关的研究,现在正致力于研究云计算技术。

本篇是本系列的最终章,将总结一下App Engine在使用方面的注意点,最佳实践和适用场景,最后会谈一下我对App Engine的一些期望。

注意点

  • 执行速度偏慢:由于其分布式的设计,所以在速度方面不是最优的,比如普通的Memcache能在几毫秒完成操作,而App Engine的Memcache则大概需要50(毫)秒才能完成操作。
  • 私有API:其API有很多都是私有,特别是在其服务方面,虽然Google提供了很不错的文档,但是在学习和移植等方面,成本都很高。
  • 执行会出现失败的情况:根据很多人的实际经验,App Engine会不定时出现执行失败的情况,特别是Datastore和URLFetch这两部分,虽然Google已经将Datastore方面出现错误的几率从原先的0.4降至现在的0.1,但是失败的情况是很难避免的。
  • 有时会停机:虽然总体而言,停机并不频繁,但是在今年初出现长达136分钟故障导致部分用户的应用无法正常运行,其发生原因来自于其备份数据中心出现了问题。
  • 无法选择合适的数据中心:比如,你应用所面对的用户主要在欧洲,但是你应用所属App Engine服务器却很有可能是被部署在一个美国的数据中心内,虽然你的应用很有可能在将来移动至欧洲某个数据中心,但是你却无法控制整个过程。
  • 有时会处理请求超时:虽然能平均在100至200ms之间完成海量的请求,但是有时会出现处理请求超时的情况。
  • 不支持裸域名:只支持类似CNAME的子域名。

最佳实践

  • 适应App Engine的数据模型:因为其数据模型,并不是传统的关系模式,而且在性能方面表现也和关系型数据库差别很大,所以如果想要用好非常关键的Datastore,那么理解和适应其数据模型是不可或缺的。
  • 对应用进行切分:由于App Engine对每个应用都有一定资源限制,而且为了让应用更SOA化和更模块化,可以对一个应用切分多个子应用,比如,可以分成一个用于前端的Web应用和多个用于REST服务的后台应用。
  • 极可能多地利用Memcache,这样不仅能减少昂贵的Datastore操作,而且能减轻Datastore的压力。
  • 在上面提到过,由于App Engine在执行某些操作时会出现失败的情况,比如Datastore方面,所以要在设计和实现这两方面做好相应的异常处理工作。
  • 由于Datastore不是关系型数据库,导致在执行常见的求总数操作时显的有点”捉襟见肘”,所以最好使用Google推荐的Sharded Counters技术来计算总数。
  • 由于Blobstore还只是刚走出试验期而已,而且其他模块对静态文件(比如图片等)支持不佳,比如Datastore只支持1MB以内的对象,同时每个应用只能最多上传一千个文件,而且速度不是最优,所以推荐使用其他专业的云存储,比如Amazon的S3或者Google马上就要推出的Google Storage等。
  • 尽量使用批处理方式,不论是在使用Datastore还是发送邮件等。
  • 不要手动创建Index:因为App Engine会自动根据你在代码中查询来创建相关的Index。

适用场景

现在而言,App Engne主要适用于下面这三个场景:

  • Web Hosting:这是最常见的场景,在App Engine上已经部署了数以十万计的小型网站(其中有很多主要为了学习目的),而且还部署了一些突发流量很大的网站,其中最著名的例子就是美国白宫的”Open For Questions”这个站点,主要用于让美国人民给奥巴马总统提问的,这个站点在短短的几个小时内处理接近百万级别的流量。
  • REST服务:这也是在App Engine平台上很常见的场景,最出名的例子就是BuddyPoke,BuddyPoke的客户端就是一个Flash应用,在用户的浏览器上运行,而它的服务器端则是以REST服务的形式放置在App Engine上,每当Flash客户端需要读取和存储数据的时候,它都会发请求给后端的REST服务,来让其执行相关的Datastore操作。
  • 依赖Google服务的应用:比如应用能够通过App Engine的Email服务来发送大规模的电子邮件。

未来的期望

  • 更稳定的表现,更少的超时异常和更快的反应速度,特别是在Datastore和Memcached这两方面。
  • 支持对数据中心的选择,虽然现在App Engine会根据应用的用户群的所在地来调整应用所在的数据中心,但由于整个过程对开发者而言是不可控的,所以希望能在创建应用的时候,能让用户自己选择合适的数据中心。
  • SLA,如果App Engine能像S3那样设定一些SLA条款,这样将使用户更放心地在App Engine上部署应用。
  • 新的语言:比如PHP,但是如果在现有的App Engine架构上添加一门新的语言,整个工作量会非常大的,因为App Engine有接近一半的模块是语言特定的,比如应用服务器和开发环境等,所以短期内我认为不太可能支持新的语言。

总体而言,Google App Engine是Google大战略中一个不可分割的一部分,因为Google希望能通过App Engine来降低Web应用开发的难度,只要难度降低了,那么Web应用替代客户端应用的整体速度将会加快,如果出现这样的情况的话,那么将会对Google今后的发展非常有利。

本系列文章结束。

参考资料:

EOF

探索Google App Engine背后的奥秘(5)- Datastore的设计

按:此为客座博文系列。投稿人吴朱华曾在IBM中国研究院从事与云计算相关的研究,现在正致力于研究云计算技术。

本篇会首先会从程序员角度来介绍一下Datastore在使用方面的一些信息,之后会接着介绍Datastore是如何构建的。

使用方面

首先,在编程方面,Datastore是基于”Entity(实体)”这个概念,而且Entity和”对象”这个概念比较类似,同时Entity可以包括多个Property(属性),Property的类别有整数,浮点和字符串等,比如,可以设计一个名为”Person”的Entity,它包含名为”Name”的字符串Property和名为”Age”的整数Property。由于Datastore是”Schema-less”的,所以数据的Schema都由应用维护,而且能非常方便地对一个Entity所包含的属性进行增删和修改。在存储方面,一个Entity的实例可以被认为是一个普通的”Row(行)”,而包含所有这种Entity的实例的Table被称为Kind,比如,所有通过”Person”这个Entity生成实例,比如小吴,小朱和小华等,它们都会存放在同一个名为”Person”的Kind中。在结构方面,虽然也能通过特定的方式在Datastore中实现关系型结构,但是Datastore在设计上是为层次(Hierarchical)性结构”度身定做”的,有Root Entity和Child Entity之分,比如,可以把”Person”作为Root Entity(父实体),”Address”作为”Person”的Child Entity,两者合在一起可以称为一个”Entity Group”。这样做的好处是能将这两个实体集中一个BigTable本地分区中,而且能对这两个实体进行本地事务。

接下来,将谈一下Datastore支持那些高级功能:其一是提供名为GQL(Google Query Language)的查询语言,GQL是SQL的一个非常小的子集,包括对”>”,”<“和”=”等操作符。其二是App Engine会根据代码中查询语句来自动生成相应Index,但不支持对Composite Index生成。其三是虽然由于Datastore分布式的设计,所以在速度方面和传统的关系型数据库相比一定的差距,但是Google的架构师保证大部分对Datastore的操作能在200ms之内完成,同时也得益于它的分布式设计,使得它在扩展性方面特别出色。其四是Datastore也支持在实体之间创建关系,比如在Python版App Engine中可以使用ReferenceProperty在实体间构建一对多和多对多的关系。

下表为Datastore和传统的关系型数据库之间的比较:

  Datastore 关系型数据库
SQL支持 只支持一些基本的查询 全部支持
主要结构 层次(Hierarchical) 关系
Index 部分可自动创建 手动创建
事务 只支持在一个Entity Group内执行 支持
平均执行速度(ms) 低于200 低于100
扩展型 非常好 很困难,而且需要进行大量的修改

表1. Datastore和关系型数据库之间的比较

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

实现方面

在实现方面,Datastore是在BigTable的基础上构建的,所以本段会首先重新介绍一下BigTable,之后会介绍Datastore的两个组成部分:Entities Table和Index,最后会讲一下它在事务和备份这两方面所采用的机制。

BigTable

在本系列的第一篇已经按照Google的Paper对BigTable技术做了一定的介绍,但其实BigTable本身其实没有之前介绍的那样复杂,其实就是一个非常巨大的Table,这也是是它之所以名为”BigTable”的原因,而且结构就像图1那样非常简单,就是一个个ROW,每个ROW都有一个Name和一组Cloumn,但是为了支持海量的数据,它将这个大的Table进行分片(Sharding)处理,每台服务器存储一个海量的Table的一小部分,并且为了查询效率,会对这个Table进行排序。就像App Engine的创始人之一Ryan Barrett所说的那样”BigTable is a sharded, sorted array “。

BigTable Simple.PNG

图1. BigTable简化版模型

在功能方面,首先,BigTable支持基本的CRUD操作,也就是增加(Create),查询(Read),更新(Update)和删除(Delete)。其次支持对Single-Row的事务与基于前缀和范围的扫描。

Entities Table

它是Datastore最核心的Table,是以BigTable的形式存在的,主要用于存储所有的Entity,而且是格式非常简单,每行都会有一个Row Name,也称为Entity Key(可认为它是一个Entity的Primary Key),而且只有唯一一个Column,主要用于存放被序列化的Entity。每个Entity的Key的生成是基于它的父Entity(如果有的话)和其父至上的Entity,直到其Root Entity。以下图为例,timmy的父Entity是jane,jane的父Entity兼Root Entity是Ethel,所以最后timmy的Entity Key是”/Grandparent:Ethel/Parent:Jane/Child:Timmy”。

entity keys.PNG

图2. Entity Key的例子

Index

Index主要是为方便和加速查询而生的,所以在切入Index之前,先介绍一下Datastore主要支持那些查询,主要有三类:其一是基于Kind的,其二是基于Property值的,其三是基于多个Property值的。

Index表也是以BigTable的形式存在,但是和上面的Entities Table是分离的,主要用来单独存放那些需要被Index的数据,而且由于怕Index表体积太大,所以不会有时将其放置在内存中以提升查询速度。

主要有下面这几种Index表:

  • Kind Index:用于加速那些用于获取所有属于某个Kind的Entity的查询,比如把所有属于Person这个Kind的Entity,包括小吴,小朱和小华等提取出来,Kind Index表每行有Kind和Entity Key这两个列,此Index会有系统自动生成。
  • Single-property Index:用于加速那些基于单一属性值的查询,比如要找出所有Age在20之下的Person,Age就是所谓的那个单一属性值,Single-property Index表每行除了Kind和Entity Key之外,还有属性名和属性值这两个列,此Index也会有系统自动生成,还会根据升降序的不同,生成两个表。
  • Composite Index:用于加速那些基于对多个属性值的查询,Composite Index表基本和上面的Single-property Index表非常类似,但是每行包括多个属性名和属性值,而且由于此Index消耗资源非常多,所有由开发人自己确定是不是需要这个Index,系统不自动生成。

事务

原则上所有对单一Entity的Write操作都是事务的,并基于上面提到的BigTable的Single-Row事务和Optimistic Concurrency Control这两个技术,下面是流程:首先,系统会读这个Entity的Committed Timestamp(提交时间戳),Write会以串行(Serialized)的形式写入到BigTable的日志中,之后,系统会将日志更新到BigTable的表中,如果成功的话,系统会更新这个Entity的Committed Timestamp,但如果系统发现在更新之前,Committed Timestamp发生了变化,也就是说另一个事务在这个事务执行过程中已经对这个Entity进行了操作,在这个时候,系统会重新执行这个事务。由于在整个事务过程采用Optimistic Concurrency Control,而不是Locking,所以在吞吐量方面表现不错。

如果要对多个Entity执行事务,那就需要将这几个Entity设为一个Entity Group,也就意味着将这几个Entity放在同一台物理机上。在执行的时候,会将以Root Entity的Committed Timestamp为准来对所有参与事务的Entity进行和上面差不多的事务操作。

备份

与BigTable基于Row级别的备份不同的是,Datastore是基于Enity Group级别,而且采用Paxos算法,所以Datastore的备份方法比BigTable的更安全。

总体而言,Datastore在设计理念上和传统的关系型数据库有很大的不同,所以其在反应速度和写数据方面不是最优的,但是现在Web应用以读为主,而且需要能通过简单的扩展就能支持其海量的数据,而这两点却是Datastore所擅长,所以Datastore非常适合支撑Web应用。

本篇结束,下篇是本系列的总结。

EOF

探索Google App Engine背后的奥秘(4)- Google App Engine的架构

按:此为客座博文系列。投稿人吴朱华曾在IBM中国研究院从事与云计算相关的研究,现在正致力于研究云计算技术。

本篇将首先介绍App Engine的一些设计理念,接着将对App Engine的组成部分等进行介绍。

设计理念

App Engine在设计理念方面,主要可以总结为下面这五条:

  • 重用现有的Google技术:大家都知道,重用是软件工程的核心理念之一,因为通过重用不仅能减低开发成本,而且能简化架构。在App Engine开发的过程中,重用的思想也得到了非常好的体现,比如Datastore是基于Google的bigtable技术,Images服务是基于Picasa的,用户认证服务是利用Google Account的,Email服务是基于Gmail的等。
  • 无状态:为了让更好地支持扩展,Google没有在应用服务器层存储任何重要的状态,而主要在datastore这层对数据进行持久化,这样当应用流量突然爆发时,可以通过为应用添加新的服务器来实现扩展。
  • 硬限制:App Engine对运行在其之上的应用代码设置了很多硬性限制,比如无法创建Socket和Thread等有限的系统资源,这样能保证不让一些恶性的应用影响到与其临近应用的正常运行,同时也能保证在应用之间能做到一定的隔离。
  • 利用Protocol Buffers技术来解决服务方面的异构性:应用服务器和很多服务相连,有可能会出现异构性的问题,比如应用服务器是用Java写的,而部分服务是用C++写的等。Google在这方面的解决方法是基于语言中立,平台中立和可扩展的Protocol Buffer,并且在App Engine平台上所有API的调用都需要在进行RPC(Remote Procedure Call,远程方面调用)之前被编译成Protocol Buffer的二进制格式。
  • 分布式数据库:因为App Engine将支撑海量的网络应用,所以独立数据库的设计肯定是不可取的,而且很有可能将面对起伏不定的流量,所以需要一个分布式的数据库来支撑海量的数据和海量的查询。

组成部分

GAE ARCH.jpg

图1. GAE的架构图(图源自参[6])

简单而言,其架构可以分为三个部分:前端,Datastore和服务群:

前端

共包括四个模块:

  • Front End:既可以认为它是Load Balancer,也可以认为它是Proxy,它主要负责负载均衡和将请求转发给App Server(应用服务器)或者Static Files等工作。
  • Static Files:在概念上,比较类似于CDN(Content Delivery Network,内容分发网络),用于存储和传送那些应用附带的静态文件,比如图片,CSSJS脚本等。
  • App Server:用于处理用户发来的请求,并根据请求的内容来调用后面的Datastore和服务群。
  • App Master:是在应用服务器间调度应用,并将调度之后的情况通知Front End。

Datastore

它是基于BigTable技术的分布式数据库,虽然其也可以被理解成为一个服务,但是由于其是整个App Engine唯一存储持久化数据的地方,所以其是App Engine中一个非常核心的模块。其具体细节将在下篇和大家讨论。

服务群

整个服务群包括很多服务供App Server调用,比如Memcache,图形,用户,URL抓取和任务队列等。

Python版和Java版App Engine在实现方面的区别

因为大多数服务都可以被这两个版本共享,所以两者之间的区别主要集中在App Server端,Python版App Server应该是经过Google修改的Python Runtime,版本号应该是2.5.2,而Java版App Server是基于Jetty 6的,因为它的体积和最常用的Tomcat相比更娇小,这样能使得一台服务器支持更多的应用,而且其应该经过Google的一定的修改。

流程

在这里举一个普通的HTTP请求的处理流程为例:

  • 用户发送一个HTTP请求。
  • Front End接受这个请求,并将这个请求转发给一个空闲的App Server。
  • App Server会处理这个请求。
  • 检查用于处理这个请求的Handler是不是已经被初始化了,如果没有的话,需要对这个Handler进行初始化。
  • 调用服务群的用户认证服务来对用户进行认证,如果失败的话,需要终止整个请求的处理工作,并返回用户无法被认证的信息。
  • 查看这个请求所需的数据是否已经缓存在Memcahe中,如果没有的话,将对Datastore发出查询请求来得到数据。
  • 通过整合上步得到数据来生成相关的HTML,并返回给用户。
  • 由于HTML里面会包含对一些静态文件的引用,比如图片和CSS等,所以当用户收到HTML之后,还会通过Front End对Static Files里面存储的静态文件进行读取。

本篇结束,下篇将关注App Engine最核心的Datastore的设计。

EOF