用户注册



邮箱:

密码:

用户登录


邮箱:

密码:
记住登录一个月忘记密码?

发表随想


还能输入:200字

小蜜锋    -  云代码空间

—— 技术宅拯救世界!

Google App Engine背后的奥秘

2013-07-30|4553阅||

摘要:本系列文章基于公开资料对Google App Engine的实现机制这个话题进行深度探讨。在切入Google App Engine之前,首先会对Google的核心技术和其整体架构进行分析,以帮助大家之后更好地理解Google App Engine的实现。 本篇将主要介绍Go

本系列文章基于公开资料对Google App Engine的实现机制这个话题进行深度探讨。在切入Google App Engine之前,首先会对Google的核心技术和其整体架构进行分析,以帮助大家之后更好地理解Google App Engine的实现。

本篇将主要介绍Google的十个核心技术,而且可以分为四大类:

  • 分布式基础设施:GFS、Chubby 和 Protocol Buffer。
  • 分布式大规模数据处理:MapReduce 和 Sawzall。
  • 分布式数据库技术:BigTable 和数据库 Sharding。
  • 数据中心优化技术:数据中心高温化、12V电池和服务器整合。

 

分布式基础设施

GFS

由于搜索引擎需要处理海量的数据,所以Google的两位创始人Larry Page和Sergey Brin在创业初期设计一套名为”BigFiles”的文件系统,而GFS(全称为”Google File System”)这套分布式文件系统则是”BigFiles”的延续。

首先,介绍它的架构,GFS主要分为两类节点:

  • Master节点:主要存储与数据文件相关的元数据,而不是Chunk(数据块)。元数据包括一个能将64位标签映射到数据块的位置及其组成文件的表格,数据块副本位置和哪个进程正在读写特定的数据块等。还有Master节点会周期性地接收从每个Chunk节点来的更新(”Heart-beat”)来让元数据保持最新状态。
  • Chunk节点:顾名思义,肯定用来存储Chunk,数据文件通过被分割为每个默认大小为64MB的Chunk的方式存储,而且每个Chunk有唯一一个64位标签,并且每个Chunk都会在整个分布式系统被复制多次,默认为3次。

下图就是GFS的架构图:

图1. GFS的架构图(参片[15])

接着,在设计上,GFS主要有八个特点:

  • 大文件和大数据块:数据文件的大小普遍在GB级别,而且其每个数据块默认大小为64MB,这样做的好处是减少了元数据的大小,能使Master节点能够非常方便地将元数据放置在内存中以提升访问效率。
  • 操作以添加为主:因为文件很少被删减或者覆盖,通常只是进行添加或者读取操作,这样能充分考虑到硬盘线性吞吐量大和随机读写慢的特点。
  • 支持容错:首先,虽然当时为了设计方便,采用了单Master的方案,但是整个系统会保证每个Master都会有其相对应的复制品,以便于在Master节点出现问题时进行切换。其次,在Chunk层,GFS已经在设计上将节点失败视为常态,所以能非常好地处理Chunk节点失效的问题。
  • 高吞吐量:虽然其单个节点的性能无论是从吞吐量还是延迟都很普通,但因为其支持上千的节点,所以总的数据吞吐量是非常惊人的。
  • 保护数据:首先,文件被分割成固定尺寸的数据块以便于保存,而且每个数据块都会被系统复制三份。
  • 扩展能力强:因为元数据偏小,使得一个Master节点能控制上千个存数据的Chunk节点。
  • 支持压缩:对于那些稍旧的文件,可以通过对它进行压缩,来节省硬盘空间,并且压缩率非常惊人,有时甚至接近90%。
  • 用户空间:虽然在用户空间运行在运行效率方面稍差,但是更便于开发和测试,还有能更好利用Linux的自带的一些POSIX API。

现在Google内部至少运行着200多个GFS集群,最大的集群有几千台服务器,并且服务于多个Google服务,比如Google搜索。但由于GFS主要为搜索而设计,所以不是很适合新的一些Google产品,比YouTube、Gmail和更强调大规模索引和实时性的Caffeine搜索引擎等,所以Google已经在开发下一代GFS,代号为”Colossus”,并且在设计方面有许多不同,比如:支持分布式Master节点来提升高可用性并能支撑更多文件,Chunk节点能支持1MB大小的chunk以支撑低延迟应用的需要。

Chubby

简单的来说,Chubby 属于分布式锁服务,通过 Chubby,一个分布式系统中的上千个client都能够对于某项资源进行”加锁”或者”解锁”,常用于BigTable的协作工作,在实现方面是通过对文件的创建操作来实现”加锁”,并基于著名科学家Leslie Lamport的Paxos算法。

Protocol Buffer

Protocol Buffer,是Google内部使用一种语言中立、平台中立和可扩展的序列化结构化数据的方式,并提供 Java、C++ 和 Python 这三种语言的实现,每一种实现都包含了相应语言的编译器以及库文件,而且它是一种二进制的格式,所以其速度是使用 XML 进行数据交换的10倍左右。它主要用于两个方面:其一是RPC通信,它可用于分布式应用之间或者异构环境下的通信。其二是数据存储方面,因为它自描述,而且压缩很方便,所以可用于对数据进行持久化,比如存储日志信息,并可被Map Reduce程序处理。与Protocol Buffer比较类似的产品还有Facebook的 Thrift ,而且 Facebook 号称Thrift在速度上还有一定的优势。

分布式大规模数据处理

MapReduce

首先,在Google数据中心会有大规模数据需要处理,比如被网络爬虫(Web Crawler)抓取的大量网页等。由于这些数据很多都是PB级别,导致处理工作不得不尽可能的并行化,而Google为了解决这个问题,引入了MapReduce这个编程模型,MapReduce是源自函数式语言,主要通过”Map(映射)”和”Reduce(化简)”这两个步骤来并行处理大规模的数据集。Map会先对由很多独立元素组成的逻辑列表中的每一个元素进行指定的操作,且原始列表不会被更改,会创建多个新的列表来保存Map的处理结果。也就意味着,Map操作是高度并行的。当Map工作完成之后,系统会先对新生成的多个列表进行清理(Shuffle)和排序,之后会这些新创建的列表进行Reduce操作,也就是对一个列表中的元素根据Key值进行适当的合并。

下图为MapReduce的运行机制:

图2. MapReduce的运行机制(参[19])

接下来,将根据上图来举一个MapReduce的例子:比如,通过搜索Spider将海量的Web页面抓取到本地的GFS集群中,然后Index系统将会对这个GFS集群中多个数据Chunk进行平行的Map处理,生成多个Key为URL,value为html页面的键值对(Key-Value Map),接着系统会对这些刚生成的键值对进行Shuffle(清理),之后系统会通过Reduce操作来根据相同的key值(也就是URL)合并这些键值对。

最后,通过MapReduce这么简单的编程模型,不仅能用于处理大规模数据,而且能将很多繁琐的细节隐藏起来,比如自动并行化,负载均衡和机器宕机处理等,这样将极大地简化程序员的开发工作。MapReduce可用于包括”分布grep,分布排序,web访问日志分析,反向索引构建,文档聚类,机器学习,基于统计的机器翻译,生成Google的整个搜索的索引”等大规模数据处理工作。Yahoo也推出MapReduce的开源版本Hadoop,而且Hadoop在业界也已经被大规模使用。

Sawzall

Sawzall可以被认为是构建在MapReduce之上的采用类似Java语法的DSL(Domain-Specific Language),也可以认为它是分布式的AWK。它主要用于对大规模分布式数据进行筛选和聚合等高级数据处理操作,在实现方面,是通过解释器将其转化为相对应的MapReduce任务。除了Google的Sawzall之外,yahoo推出了相似的Pig语言,但其语法类似于SQL。

分布式数据库技术

BigTable

由于在Google的数据中心存储PB级以上的非关系型数据时候,比如网页和地理数据等,为了更好地存储和利用这些数据,Google开发了一套数据库系统,名为”BigTable”。BigTable不是一个关系型的数据库,它也不支持关联(Join)等高级SQL操作,取而代之的是多级映射的数据结构,并是一种面向大规模处理、容错性强的自我管理系统,拥有TB级的内存和PB级的存储能力,使用结构化的文件来存储数据,并每秒可以处理数百万的读写操作。

什么是多级映射的数据结构呢?就是一个稀疏的,多维的,排序的Map,每个Cell由行关键字,列关键字和时间戳三维定位.Cell的内容是一个不解释的字符串,比如下表存储每个网站的内容与被其他网站的反向连接的文本。 反向的URL com.cnn.www是这行的关键字;contents列存储网页内容,每个内容有一个时间戳,因为有两个反向连接,所以archor的Column Family有两列:anchor: cnnsi.com和anchhor:my.look.ca。Column Family这个概念,使得表可以轻松地横向扩展。下面是它具体的数据模型图:

图3. BigTable数据模型图(参[4])

在结构上,首先,BigTable基于GFS分布式文件系统和Chubby分布式锁服务。其次BigTable也分为两部分:其一是Master节点,用来处理元数据相关的操作并支持负载均衡。其二是tablet节点,主要用于存储数据库的分片tablet,并提供相应的数据访问,同时Tablet是基于名为SSTable的格式,对压缩有很好的支持。

图4. BigTable架构图(参[15])

BigTable正在为Google六十多种产品和项目提供存储和获取结构化数据的支撑平台,其中包括有Google Print、 Orkut、Google Maps、Google Earth和Blogger等,而且Google至少运行着500个BigTable集群。

随着Google内部服务对需求的不断提高和技术的不断地发展,导致原先的BigTable已经无法满足用户的需求,而Google也正在开发下一代BigTable,名为”Spanner(扳手)”,它主要有下面这些BigTable所无法支持的特性:

  • 支持多种数据结构,比如table,familie,group和coprocessor等。
  • 基于分层目录和行的细粒度的复制和权限管理。
  • 支持跨数据中心的强一致性和弱一致性控制。
  • 基于Paxos算法的强一致性副本同步,并支持分布式事务。
  • 提供许多自动化操作。
  • 强大的扩展能力,能支持百万台服务器级别的集群。
  • 用户可以自定义诸如延迟和复制次数等重要参数以适应不同的需求。

数据库Sharding

Sharding就是分片的意思,虽然非关系型数据库比如BigTable在Google的世界中占有非常重要的地位,但是面对传统OLTP应用,比如广告系统,Google还是采用传统的关系型数据库技术,也就是MySQL,同时由于Google所需要面对流量非常巨大,所以Google在数据库层采用了分片(Sharding)的水平扩展(Scale Out)解决方案,分片是在传统垂直扩展(Scale Up)的分区模式上的一种提升,主要通过时间,范围和面向服务等方式来将一个大型的数据库分成多片,并且这些数据片可以跨越多个数据库和服务器来实现水平扩展。

Google整套数据库分片技术主要有下面这些优点:

  • 扩展性强:在Google生产环境中,已经有支持上千台服务器的MySQL分片集群。
  • 吞吐量惊人:通过巨大的MySQL分片集群能满足巨量的查询请求。
  • 全球备份:不仅在一个数据中心还是在全球的范围,Google都会对MySQL的分片数据进行备份,这样不仅能保护数据,而且方便扩展。

在实现方面,主要可分为两块:其一是在MySQL InnoDB基础上添加了数据库分片的技术。其二是在ORM层的Hibernate的基础上也添加了相关的分片技术,并支持虚拟分片(Virtual Shard)来便于开发和管理。同时Google也已经将这两方面的代码提交给相关组织。

数据中心优化技术

数据中心高温化

大中型数据中心的PUE(Power Usage Effectiveness)普遍在2左右,也就是在服务器等计算设备上耗1度电,在空调等辅助设备上也要消耗一度电。对一些非常出色的数据中心,最多也就能达到1.7,但是Google通过一些有效的设计使部分数据中心到达了业界领先的1.2,在这些设计当中,其中最有特色的莫过于数据中心高温化,也就是让数据中心内的计算设备运行在偏高的温度下,Google的能源方面的总监Erik Teetzel在谈到这点的时候说:”普通的数据中心在70华氏度(21摄氏度)下面工作,而我们则推荐80华氏度(27摄氏度)”。但是在提高数据中心的温度方面会有两个常见的限制条件:其一是服务器设备的崩溃点,其二是精确的温度控制。如果做好这两点,数据中心就能够在高温下工作,因为假设数据中心的管理员能对数据中心的温度进行正负1/2度的调节,这将使服务器设备能在崩溃点5度之内工作,而不是常见的20度之内,这样既经济,又安全。还有,业界传言Intel为Google提供抗高温设计的定制芯片,但云计算界的顶级专家James Hamilton认为不太可能,因为虽然处理器也非常惧怕热量,但是与内存和硬盘相比还是强很多,所以处理器在抗高温设计中并不是一个核心因素。同时他也非常支持使数据中心高温化这个想法,而且期望将来数据中心甚至能运行在40摄氏度下,这样不仅能节省空调方面的成本,而且对环境也很有利。

12V电池

由于传统的UPS在资源方面比较浪费,所以Google在这方面另辟蹊径,采用了给每台服务器配一个专用的12V电池的做法来替换了常用的UPS,如果主电源系统出现故障,将由该电池负责对服务器供电。虽然大型UPS可以达到92%到95%的效率,但是比起内置电池的99.99%而言是非常捉襟见肘的,而且由于能量守恒的原因,导致那么未被UPS充分利用的电力会被转化成热能,这将导致用于空调的能耗相应地攀升,从而走入一个恶性循环。同时在电源方面也有类似的”神来之笔”,普通的服务器电源会同时提供5V和12V的直流电。但是Google设计的服务器电源只输出12V直流电,必要的转换在主板上进行,虽然这种设计会使主板的成本增加1美元到2美元,但是它不仅能使电源能在接近其峰值容量的情况下运行,而且在铜线上传输电流时效率更高。

服务器整合

谈到虚拟化的杀手锏时,第一个让人想到肯定是服务器整合,而且普遍能实现1:8的整合率来降低各方面的成本。有趣的是,Google在硬件方面也引入类似服务器整合的想法,它的做法是在一个机箱大小的空间内放置两台服务器,这些做的好处有很多,首先,减小了占地面积。其次,通过让两台服务器共享诸如电源等设备,来降低设备和能源等方面的投入。

在软件工程界,大家有一个共识,那就是”需求决定架构”,也就是说,架构的发展是为了更好地支撑应用。那么本文在介绍架构之前,先介绍一下Google所提供的主要产品有哪些?

产品

对于Google和它几个主要产品,比如搜索和邮件等,大家已经非常熟悉了,但是其提供服务的不只于此,并主要可分为六大类:

  • 各种搜索:网页搜索,图片搜索和视频搜索等。
  • 广告系统:AdWords和AdSense。
  • 生产力工具:Gmail和Google Apps等。
  • 地理产品:地图,Google Earth和Google Sky等。
  • 视频播放:Youtube。
  • PaaS平台:Google App Engine。

设计理念

根据现有的资料,Google的设计理念主要可以总结出下面这六条:

  • Scale,Scale,Scale Scale:因为Google大多数服务所面对的客户都是百万级别以上的,导致Scale也就是伸缩已经深深植入Google的DNA中,而且Google为了帮助其开发人员更好地开发分布式应用和服务,不仅研发了用于大规模数据处理MapReduce框架,还推出了用于部署分布式应用的PaaS平台Google App Engine。
  • 容错:一个分布式系统,就算是构建在昂贵的小型机或者大型机之上,也会不时地出现软件或者硬件方面的错误,何况Google的分布式系统还是浇筑在便宜的X86服务器之上,即使其设备标称的MTBF(平均故障间隔时间)很高,但是由于一个集群内的设备极多,导致其错误发生的几率非常高,比如李开复曾经提过这样一个例子:在一个拥有两万台X86服务器的集群中,每天大约有110台机器会出现宕机等恶劣情况,所以容错是一个不可被忽视的问题,同时这点也被Google院士Jeffrey Dean在多次演讲中提到。
  • 低延迟:延迟是影响用户体验的一个非常重要的因素,Google的副总裁Marissa Mayer曾经说过:”如果每次搜索的时间多延迟半秒的话,那么使用搜索服务的人将减少20%”,从这个例子可以看出,低延迟对用户体验非常关键,而且为了避免光速和复杂网络环境造成的延时,Google已在很多地区设置了本地的数据中心。
  • 廉价的硬件和软件:由于Google每天所处理的数据和请求在规模上是史无前例的,所以现有的服务器和商业软件厂商是很难为Google”度身定做”一套分布式系统,而且就算能够设计和生产出来,其价格也是Google所无法承受的,所以其上百万台服务器基本采用便宜的X86系统和开源的Linux,并开发了一整套分布式软件栈,其中就包括上篇提到的MapReduce,BigTable和GFS等。
  • 优先移动计算:虽然随着摩尔定律的不断发展,使得很多资源都处于不断地增长中,比如带宽等,但是到现在为止移动数据成本远大于移动计算的成本,所以在处理大规模数据的时候,Google还是倾向于移动计算,而不是移动数据。
  • 服务模式:在Google的系统中,服务是相当常用的,比如其核心的搜索引擎需要依赖700-1000个内部服务,而且服务这种松耦合的开发模式在测试,开发和扩展等方面都有优势,因为它适合小团队开发,并且便于测试。

整体架构的猜想

在整体架构这部分,首先会举出Google的三种主要工作负载,接着会试着对数据中心进行分类,最后会做一下总结。

三种工作负载

对于Google而言,其实工作负载并不仅仅只有搜索这一种,主要可以被分为三大类:

  • 本地交互:用于在用户本地为其提供基本的Google服务,比如网页搜索等,但会将内容的生成和管理工作移交给下面的内容交付系统,比如:生成搜索所需的Index等。通过本地交互,能让用户减少延迟,从而提高用户体验,而且其对SLA要求很高,因为是直接面对客户的。
  • 内容交付:用于为Google大多数服务提供内容的存储,生成和管理工作,比如创建搜索所需的Index,存储YouTube的视频和GMail的数据等,而且内容交互系统主要基于Google自己开发那套分布式软件栈。还有,这套系统非常重视吞吐量和成本,而不是SLA。
  • 关键业务:主要包括Google一些企业级事务,比如用于企业日常运行的客户管理和人力资源等系统和赚取利润的广告系统(AdWords和AdSense),同时关键业务对SLA的要求非常高。

两类数据中心

按照2008年数据,Google在全球有37个数据中心,其中19个在美国,12个在欧洲,3个在亚洲(北京、香港、东京),另外3个分布于俄罗斯和南美。下图显示其中36个数据中心在全球的分布:

探索Google App Engine背后的奥秘

2008年Google全球数据中心分布图

  根据Jeffrey Dean在2009年末的一次演讲和最近几期季报可以推测出Google并没有在2009年过多地增加全球数据中心的数量,总数应该还是稍多于36个,但很有可能在台湾、马来西亚、立陶宛等地增加新的数据中心。

虽然Google拥有数据中心数量很多,但是它们之间存在一定的差异,而且主要可以分为两类:其一是巨型数据中心,其二是大中型数据中心。

巨型数据中心:服务器规模应该在十万台以上,常坐落于发电厂旁以获得更廉价的能源,主要用于Google内部服务,也就是内容交付服务,而且在设计方面主要关注成本和吞吐量,所以引入了大量的定制硬件和软件,来减低PUE并提升处理量,但其对SLA方面要求不是特别严厉,只要保证绝大部分时间可用即可。下图是Google巨型数据中心的一个代表,这个数据中心位于美国俄勒冈州北部哥伦比亚河畔的Dalles市,总占地面积接近30英亩,并占用了附近一个1.8GW水力发电站的大部分电力输出,当这个数据中心全部投入使用后,将消耗103兆瓦的电力,这相当于一个中小型城市的整个生活用电。

探索Google App Engine背后的奥秘

Google在美国俄勒冈州哥伦比亚河畔的巨型数据中心近景图

大中型数据中心:服务器规模在千台至万台左右,可用于本地交互或者关键业务,在设计方面上非常重视延迟和高可用性,使得其坐落地点尽可能地接近用户而且采用了标准硬件和软件,比如Dell的服务器和MySQL的数据库等,常见的PUE大概在1.5和1.9之间。本来坐落于北京朝阳区酒仙桥附近的”世纪互联”机房的Google中国数据中心也属于大中型数据中心这类,其采用的硬件有DELL的工作站和Juniper的防火墙等,下图为其一角。

探索Google App Engine背后的奥秘

Google前中国数据中心的一角(参[26])

关于两者的区别:具体请查看下表:

巨型数据中心 大中型数据中心
工作负载 内容交付 本地交互/关键业务
地点 离发电厂近 离用户近
设计特点 高吞吐,低成本 低延迟,高可用性
服务器定制化
SLA 普通
服务器数量 十万台以上 千台以上
数据中心数量 十个以内 几十个
PUE估值 1.2 1.5

巨型与大中型数据中心的对比表

总结

最后,稍微总结一下,首先,普通的用户当访问Google服务时,大多会根据其请求的IP地址或者其所属的ISP将这个请求转发到用户本地的数据中心,如果本地数据中心无法处理这个请求,它很有可能将这个请求转发给远端的内容交互中心。其次,当广告客户想接入Google的广告系统时,这个请求会直接转发至其专业的关键业务数据中心来处理。

探索Google App Engine背后的奥秘

通过前面两篇介绍,大家应该对Google强大的基础设施有一定的了解。本篇开始介绍构筑在这强大基础设施之上的Google App Engine。

Google App Engine的介绍

由于发布S3和EC2这两个优秀的云服务,使得Amazon已经率先在云计算市场站稳了脚跟,而身为云计算这个浪潮的发起者之一的Google肯定不甘示弱,并在2008年四月份推出了Google App Engine这项PaaS服务,虽然现在无法称其为一个革命性的产品,但肯定是现在市面上最成熟,并且功能最全面的PaaS平台。

Google App Engine 提供一整套开发组件来让用户轻松地在本地构建和调试网络应用,之后能让用户在Google强大的基础设施上部署和运行网络应用程序,并自动根据应用所承受的负载来对应用进行扩展,并免去用户对应用和服务器等的维护工作。同时提供大量的免费额度和灵活的资费标准。在开发语言方面,现支持Java和Python这两种语言,并为这两种语言提供基本相同的功能和API。

功能在功能上,主要有六个方面:

  • 动态网络服务,并提供对常用网络技术的支持,比如SSL等 。
  • 持久存储空间,并支持简单的查询和本地事务。
  • 能对应用进行自动扩展和负载平衡。
  • 一套功能完整的本地开发环境,可以让用户在本机上对App Engine进行开发和调试。
  • 支持包括Email和用户认证等多种服务。
  • 提供能在指定时间和定期触发事件的计划任务和能实现后台处理的任务队列。

使用流程

整个使用流程主要包括五个步骤:

  • 下载SDK和IDE,并在本地搭建开发环境。
  • 在本地对应用进行开发和调试。
  • 使用GAE自带上传工具来将应用部署到平台上。
  • 在管理界面中启动这个应用。
  • 利用管理界面来监控整个应用的运行状态和资费。

由于本系列是专注于GAE的实现和设计两方面,所以不会对GAE的使用有非常深入地介绍,如果希望大家对GAE的使用方面有更深的理解,具体可以参看一下GAE的官方文档

Google App Engine的主要组成部分

主要可分为五部分:

  • 应用服务器:主要是用于接收来自于外部的Web请求。
  • Datastore:主要用于对信息进行持久化,并基于Google著名的BigTable技术。
  • 服务:除了必备的应用服务器和Datastore之外,GAE还自带很多服务来帮助开发者,比如:Memcache,邮件,网页抓取,任务队列,XMPP等。
  • 管理界面:主要用于管理应用并监控应用的运行状态,比如,消耗了多少资源,发送了多少邮件和应用运行的日志等。
  • 本地开发环境:主要是帮助用户在本地开发和调试基于GAE的应用,包括用于安全调试的沙盒,SDK和IDE插件等工具。

应用服务器

应用服务器依据其支持语言的不同而有不同的实现。

Python的实现

Python版应用服务器的基础就是普通的Python 2.5.2版的Runtime,并考虑在在未来版本中添加对Python 3的支持,但是因为Python 3对Python而言,就好比Java2之于Java1,跨度非常大,所以引入Python3的难度很大。在Web技术方面,支持诸如Django,CherryPy,Pylons和Web2py等Python Web框架,并自带名为”WSGI”的CGI框架。虽然Python版应用服务器是基于标准的Python Runtime,但是为了安全并更好地适应App Engine的整体架构,对运行在应用服务器内的代码设置了很多方面的限制,比如不能加载用C编写Python模块和无法创建Socket等。

Java的实现

在实现方面,Java版应用服务器和Python版基本一致,也是基于标准的Java Web容器,而且选用了轻量级的Jetty技术,并跑在Java 6上。通过这个Web容器不仅能运行常见的Java Web技术,包括Servlet,JSP,JSTL和GWT等,而且还能跑大多数常用的Java API(App Engine有一个The JRE Class White List来定义哪些Java API能在App Engine的环境中被使用)和一些基于JVM的脚本语言,例如JavaScript,Ruby或Scala等,但同样无法创建Socket和Thread,或者对文件进行读写,也不支持一些比较高阶的API和框架,包括JDBC,JSF,Struts 2,RMI,JAX-RPC和Hibernate等。

Datastore

Datastore提供了一整套强大的分布式数据存储和查询服务,并能通过水平扩展来支撑海量的数据。但Datastore并不是传统的关系型数据库,它主要以”Entity”的形式存储数据,一个Entity包括一个Kind(在概念上和数据库的Table比较类似)和一系列属性。

Datastore提供强一致性和乐观(optimistic)同步控制,而在事务方面,则支持本地事务,也就是在只能同一个Entity Group内执行事务。

在接口方面,Python版提供了非常丰富的接口,而且还包括名为GQL的查询语言,而Java版则提供了标准的JDO和JPA这两套API。

而且Google已经在今年的Google I/O大会上宣布将在未来的App Engine for Business套件中包含标准的SQL数据库服务,但现在还不确定这个SQL数据库的实现方式,是基于开源的MySQL技术,还是基于其私有的实现,这是一个问题。

服务

Memcache

Memcache是大中型网站所备的服务,主要用来在内存中存储常用的数据,而App Engine也包含了这个服务。有趣的是App Engine的Memcache也是由Brad Fitzpatrick开发。

URL抓取(Fetch)

App Engine的应用可以通过URL抓取这个服务抓取网上的资源,并可以这个服务来与其他主机进行通信。这样避免了应用在Python和Java环境中无法使用Socket的尴尬。

Email

App Engine应用使用这个服务来利用Gmail的基础设施来发送电子邮件。

计划任务(Cron)

计划服务允许应用在指定时间或按指定间隔执行其设定的任务。这些任务通常称为Cron job。

图形

App Engine 提供了使用专用图像服务来操作图像数据的功能。图像服务可以调整图像大小,旋转、翻转和裁剪图像。它还能够使用预先定义的算法提升图片的质量。

用户认证

App Engine的应用可以依赖Google帐户系统来验证用户。App Engine还将支持OAuth。

XMPP

  在App Engine上运行的程序能利用XMPP服务和其他兼容XMPP的IM服务(比如Google Talk)进行通信。

任务队列(Task Queue)

App Engine应用能通过在一个队列插入任务(以Web Hook的形式)来实现后台处理,而且App Engine会根据调度方面的设置来安排这个队列里面的任务执行。

Blobstore

因为Datastore最多支持存储1MB大小的数据对象,所以App Engine推出了Blobstore服务来存储和调用那些大于1MB但小于2G的二进制数据对象。

Mapper

Mapper可以认为就是”Map Reduce”中的Map,也就是能通过Mapper API对大规模的数据进行平行的处理,这些数据可以存储在Datastore或者Blobstore,但这个功能还处于内部开发阶段。

Channel

其实Channel就是我们常说的”Comet”,通过Channel API能让应用将内容直接推至用户的浏览器,而不需常见的轮询。

除了Java版的Memcache,Email和URL抓取都是采用标准的API之外,其他服务无论是Java版还是Python版,其API都是私有的,但是提供了丰富和细致的文档来帮助用户使用。

管理界面

用了让用户更好地管理应用,Google提供了一整套完善的管理界面,地址是http://appengine.google.com/ ,而且只需用户的Google帐户就能登录和使用。下图为其截屏:

探索Google App Engine背后的奥秘

管理界面

  使用这个管理界面可执行许多操作,包括创建新的应用程序,为这个应用设置域名,查看与访问数据和错误相关的日志,观察主要资源的使用状况。

本地开发环境

为了安全起见,本地开发环境采用了沙箱(Sandbox)模式,基本上和上面提到的应用服务器的限制差不多,比如无法创建Socket和Thread,也无法对文件进行读写。Python版App Engine SDK是以普通的应用程序的形式发布,本地需要安装相应的Python Runtime,通过命令行方式启动Python版的Sandbox,同时也可以在安装有PyDev插件的Eclipse上启动。Java版App Engine SDK是以Eclispe Plugin形式发布,只要用户在他的Eclipse上安装这个Plugin,用户就能启动本地Java沙箱来开发和调试应用。

编程模型

因为App Engine主要为了支撑Web应用而存在,所以Web层编程模型对于App Engine也是最关键的。App Engine主要使用的Web模型是CGI,CGI全称为”Common Gateway Interface”,它的意思非常简单,就是收到一个请求,起一个进程或者线程来处理这个请求,当处理结束后这个进程或者线程自动关闭,之后是不断地重复这个流程。由于CGI这种方式每次处理的时候,都要重新起一个新的进程或者线程,可以说在资源消耗方面还是很厉害的,虽然有线程池(Thread Pool)这样的优化技术。但是由于CGI在架构上的简单性使其成为GAE首选的编程模型,同时由于CGI支持无状态模式,所以也在伸缩性方面非常有优势。而且App Engine的两个语言版本都自带一个CGI框架:在Python平台为WSGI。在Java平台则为经典的Servlet。最近,由于App Engine引入了计划任务和任务队列这两个特性,所以App Engine已经支持计划任务和后台进程这两种编程模型。

限制和资费

首先,谈一下App Engine的使用限制,具体请看下表:

类别 限制
每个开发者所拥有的项目 10个
每个项目的文件数 1000个
每个项目代码的大小 150MB
每个请求最多执行时间 30秒
Blobstore(二进制存储)的大小 1GB
HTTP Response的大小 10MB
Datastore中每个对象的大小 1MB

App Engine的使用限制

这些限制对开发者是一种障碍,但对App Engine这样的多租户环境而且却是非常重要的,因为如果一个租户的应用消耗过多的资源的话,将会影响到在临近应用的正常使用,而App Engine上面这些限制就是为了是运行在其平台上面应用能安全地运行着想,避免了一个吞噬资源或恶性的应用影响到临近应用的情况。除了安全的方面考虑之后,还有伸缩的原因,也就是说,当一个应用的所占空间(footprint)处于比较低的状态,比如少于1000个文件和大小低于150MB等,那么能够非常方便地通过复制应用来实现伸缩。

接着,谈一下资费情况,App Engine的资费情况主要有两个特点:其一是免费额度高,现有免费的额度能支撑一个中型网站的运行,且不需付任何费用。其二是资费项目非常细粒度,普通IaaS服务资费,主要就是CPU,内存,硬盘和网络带宽这四项,而App Engine则除了常见的CPU和网络带宽这两项之外,还包括很多应用级别的项目,比如:Datastore API和邮件API的调用次数等。具体资费的机制是这样的:如果用户的应用每天消费的各种资源都低于这个额度,那们用户无需支付任何费用,但是当免费额度被超过的时候,用户就需要为超过的部分付费。因为App Engine整套资费标准比较复杂,所以在这里就主要介绍一下它的免费额度,具体请看下表:

类型 数量(每天)
邮件API调用 7000次
传出(outbound)带宽 10G
传入(inbound)带宽 10G
CPU时间 46个小时
HTTP请求 130万次
Datastore API 1000万次
存储的数据 1G
URL抓取的API 657千次

App Engine的免费额度表

从上面免费额度来看,除了存储数据的容量外,其它都是非常强大的。

本篇将首先介绍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将支撑海量的网络应用,所以独立数据库的设计肯定是不可取的,而且很有可能将面对起伏不定的流量,所以需要一个分布式的数据库来支撑海量的数据和海量的查询。

组成部分

探索Google App Engine背后的奥秘

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

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

前端

共包括四个模块:

  • Front End:既可以认为它是Load Balancer,也可以认为它是Proxy,它主要负责负载均衡和将请求转发给App Server(应用服务器)或者Static Files等工作。
  • Static Files:在概念上,比较类似于CDN(Content Delivery Network,内容分发网络),用于存储和传送那些应用附带的静态文件,比如图片,CSS和JS脚本等。
  • 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里面存储的静态文件进行读取。

本篇会首先会从程序员角度来介绍一下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
扩展型 非常好 很困难,而且需要进行大量的修改

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

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

实现方面

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

BigTable

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

探索Google App Engine背后的奥秘

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”。

探索Google App Engine背后的奥秘

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应用。

本篇是本系列的最终章,将总结一下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今后的发展非常有利。

本系列文章结束。

参考资料:

 转自: 吴朱华

顶 2踩 0收藏
文章评论
    发表评论

    个人资料

    • 昵称: 小蜜锋
    • 等级: 高级设计师
    • 积分: 7088
    • 代码: 757 个
    • 文章: 360 篇
    • 随想: 211 条
    • 访问: 1263 次
    • 关注

    标签

    设计模式(4)java(9)命名规范(2)广告创意(1)愤怒的小鸟(1)游戏(5)jsp(1)配置(1)Surface(1)windows(1)javabean(1)设计方法(1)开发工具(2)web(4)大数据(2)GPU(1)硬盘(1)内部结构(1)黑客(1)窃取(1)编码(1)解决方法(1)php(28)mysql(9)数据库备份(1)数据库还原(1)命令(2)数据库(1)安装(1)2012(2)世界末日(3)仙剑5前传(1)默哀(1)电源(1)女生(1)装饰器模式(2)古剑奇谭(1)电脑桌(1)史上最牛(1)编程语言(2)小米(3)电视机顶盒(1)营销策略(1)Android(8)手势(1)诺亚方舟(1)Eclipse(1)汽车(1)操作系统(1)软件(1)互联网(5)大事记(1)设计师(2)壁纸(1)古剑奇谭2(1)古剑奇谭网络版(1)云计算(2)服务器(1)框架(2)Socket(1)jquery(1)构造函数执行顺序(1)火车票(1)3D(1)数据中心(2)正则表达式(2)Web前端(1)开发框架(1)系统瘫痪(1)12306(2)cpu(1)javascript(2)开发日记(15)体育馆管理系统(15)网页设计(1)CSS3(3)腾讯(3)小游戏(1)interface(1)平板(2)面试(2)设计(5)摄影(2)数据挖掘(1)钢琴谱(1)情人节(1)陈欧体(1)程序员(3)漫画(1)UserAgent(1)iPhone(2)NoSQL(1)ui(9)越狱(1)指南(1)abstract(1)css(3)git(2)八核(2)三星(1)linux(11)数据类型(1)html5(2)UML(2)perftools(1)创意(1)logo(1)色谱(1)响应式(5)Metro(2)虚拟机(1)jvm(1)垃圾回收(1)left(1)join(1)连接查询(1)溯源系统(1)Override(1)SAE(2)WordPress(1)指针(1)链表(1)系统分析师(1)中间件(1)corba(1)static(1)无线(1)监控(1)iPad(1)Apache(2)比特币(2)命名规则(1)手机支付(1)curl(3)笔记(1)导航(1)thinkphp(1)异常导致本地路径泄漏(1)web设计(1)网络安全(1)诗句(1)4K对齐(1)代码库(1)色彩(1)动画片(1)struts2(3)漏洞(5)确认框(1)心情驿站(1)ArscEditor(1)resources.(1)apktool(1)AppKey(1)新浪微博(1)app(5)广告(3)赚钱(1)响应式布局(1)html(1)淘宝(2)微信(1)重构(5)缓存(1)破解(1)后门(1)七夕(1)SEO(2)概念设计(1)面向对象(1)bootstrap(1)性能(2)优化(1)iis(1)爬虫(1)采集(1)算法(2)文本相似度(2)cto(1)js(1)fsockopen(1)扁平化设计(2)网页(1)心情(7)小米电视(1)开箱(1)励志(2)招聘(3)命名(1)notepad++(1)python(1)配色(3)扁平化(4)ps(2)搞笑(2)创业(3)渲染(1)电影(1)模板(1)微博(1)企业家(1)公司(1)总结(1)前端(1)运营(1)变形(1)svn(4)教程(3)搜狗(1)泄密(1)双11(1)天猫(1)UC(1)启动界面(1)光棍节(1)双十一(2)物流(1)备份(1)更新(1)插入(1)插件(2)jsTree(1)(1)海量数据(1)分辨率(1)草图(1)手绘(1)速度(1)文本处理(1)实习(1)感想(1)文件(1)简历(1)65.49.2.17(1)yum(1)解决办法(1)阿里云(2)推广(1)来往(1)春运(1)LBS(1)gb2312(1)utf-8(1)log4j(1)详解(1)收购(1)私服(1)TortoiseGi(1)post(1)异常(2)flappyBird(1)应用创新大赛(1)宙斯杯(1)学习方法(1)xp(1)退役(1)安全(1)技术贴(1)flash(1)刷机(1)京东(1)电商(1)Tomcat(1)JDK(1)免费(1)长投影(1)图标(1)Photoshop(1)云端集成开发环境(1)软件开发(1)可视化(1)工具(2)OpenSSL(1)Heartbleed(1)vsftp(1)中国知网(1)学术论文(1)免费下载(1)开发(1)手册(1)速查表(1)追随战略(1)sdk(1)文章(1)发布(1)文件管理(1)沙画(1)动效(2)原型(1)感悟人生(1)哲理(1)Bash(1)类图(1)知识管理(1)Console(1)调试命令(1)rpm(1)报错(1)挂载(1)数据盘(1)云主机(1)产品经理(1)原型设计(1)mql4(1)mt4(1)ea(1)程序化交易(1)CURLOPT_PO(1)阿里云​(1)CentOS6(2)OpenSSH(1)漏洞修复(2)升级(1)安骑士(1)链克(1)

    站长推荐