Google App Engine技術架構資料大盤點

從前,有一個“傷不起”的人(就是我啦)在園子裏分享過Google的架構,老是老了點,但對不少新手還是有點幫助的。今天看到幾篇有關Google App Engine的技術架構文章,覺得比那個“傷不起”孩子總結得要全得多,索性就全部弄過來,一起分享給大家,沒看到過的同學趕緊驚喜一下吧,看到過了的同學也假裝驚喜一下嘛,呵呵。

全部文章有點長,請耐心看下去,相信程序員都是有耐心的,除了我.......

另外文章的作者是吳朱華,要轉載的同學別忘了署上他的大名。下面就開始了。

一、Google的核心技術

在切入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的架構圖:

Google-file-system.png

圖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的運行機制:

Map Reduce.PNG

圖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這個概念,使得表可以輕鬆地橫向擴展。下面是它具體的數據模型圖:

Big Table Model.PNG

圖3. BigTable數據模型圖(參[4])

在結構上,首先,BigTable基於GFS分佈式文件系統和Chubby分佈式鎖服務。其次BigTable也分爲兩部分:其一是Master節 點,用來處理元數據相關的操作並支持負載均衡。其二是tablet節點,主要用於存儲數據庫的分片tablet,並提供相應的數據訪問,同時Tablet是基於名爲SSTable的格式,對壓縮有很好的支持。

BigTable.PNG

圖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 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帳戶就能登錄和使用。下圖爲其截屏:

Dashboard.PNG 圖1. 管理界面(點擊看大圖)

使用這個管理界面可執行許多操作,包括創建新的應用程序,爲這個應用設置域名,查看與訪問數據和錯誤相關的日誌,觀察主要資源的使用狀況。

本地開發環境

爲了安全起見,本地開發環境採用了沙箱(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

表1. 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千次

表2. App Engine的免費額度表

從上面免費額度來看,除了存儲數據的容量外,其它都是非常強大的。

三、Google 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,內容分發網絡),用於存儲和傳送那些應用附帶的靜態文件,比如圖片,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裏面存儲的靜態文件進行讀取。

四、Database設計

使用方面

首先,在編程方面,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應用。

對於Googe App Engine架構的介紹就到這裏了,另外其實還有很多網站的架構也是比較經典的,像我以前介紹過的Facebook架構MySpace架構優酷架構Twitter架構Flickr架構等等,有興趣的朋友可以關注一下這方面的技術資料,因爲“雲”上來了,這些都是我們學習的基礎。

最後感謝吳朱華,膜拜一下....

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章