《架构之美》摘录四
第4章 數據增長:Facebook平臺的架構
給我看你的流程圖而隱藏你的表,我仍然莫名其妙。如果給我看你的表,那么我將不再需要你的流程圖,因為它們太明顯了。
——《The Mythical Man-Month》(人月神話)
4.1 簡介
(1).在任何情況下,Web呈現的幾乎所有面對用戶的功能歸根結底都是提供一個界面,訪問站點專有的一組核心數據。這些信息構成了幾乎所有網站的核心價值,不論它是由頂級員工研究團隊創建的還是由世界各地的用戶創建的。數據推動了用戶喜歡的產品,所以架構師圍繞數據創建了其余的傳統“N層”軟件棧(邏輯層與顯示層)。
(2).這個故事講的是Facebook的數據,以及它如何與Facebook平臺的創建一起發展。 Facebook是一個很有用的圍繞數據建立架構的例子,這個社會關系網站就是緊緊地圍繞數據,使用標準的N層棧,將數據讀出呈現出來。利用局域架構中心的用戶社會關系數據,該平臺開發了一組web服務(Facebook平臺應用編程接口,或Facebook API),一門查詢語言(Facebook查詢語言,或FQL),以及一種數據驅動的標記語言(Facebook標記語言,或FBML),目的是將應用開發者的系統與Facebook的系統結合在一起。
(3).隨著某些數據集越來越廣泛的提供出來,而且用戶要求跨越多個網和桌面應用來統一使用他們的數據,Facebook以一種受控的方式向外界開放數據,跟隨數據演進的每一步的架構選擇,以及調和數據開放與滲透在社會關系系統中特定的隱私需求的過程。它包括:
<1>.促進這些類型的集成。
<2>.將數據功能從內部棧調用移到外部可見的Web服務器上(Facebook API)。
<3>.授權訪問這個Web服務,注意保持這個社會關系系統的隱私性。
<4>.創建一種數據查詢語言,減輕這個Web服務的新客戶端的負擔(Facebook FQL)。
<5>.創建一種數據驅動的標記語言,將應用的顯示集成回Facebook,同時也支持使用其他方式不能訪問的數據(Facebook FBML)。 當我們將應用的架構從分離的棧進行了足夠的演進之后:
<6>.創建一些技術來彌補Facebook體驗與外部應用體驗之間的差異。
4.1.1 某些應用核心數據
Web應用,即使是不提供也不使用任何的數據平臺,基本上仍然是由它們內部的數據來驅動的。以http://fettermansbooks.com為例,它是個假想的網站,提供書籍方面的信息。這個站點的功能可能包括可查找的庫存索引、關于每件產品的基本信息,以及用戶每本書作出的評論。訪問這些具體的信息構成了這個應用的核心,驅動了架構的其它部分。該站點可能使用Flsh和AJAX技術,支持通過移動設備來訪問,并提供一個一流的用戶界面。
4.1.2 一些Facebook核心數據
隨著所謂“Web 2.0”的網絡技術逐漸流行,數據在系統中的地位就變得更明顯了。Web 2.0展現的核心主題就是它們數據驅動的,用戶本身提供了絕大部分的數據。
4.1.3 Facebook的應用平臺
(1).在一般的n層架構中,應用將輸入(對于Web來說,就是GET、POST和Cookie信息的集合)映射為對原始數據的請求,這些原始數據可能存在于數據庫中。它們被轉換為內存中的數據,并通過一些業務邏輯進行智能化處理。輸出模塊將針對這些數據對象進行轉換,變成HTML\JavaScript\CSS等。
(2).Facebook平臺的技術通過在社會關系網絡和數據架構方面的一系列改進,實現了這一點:
<1>.應用可以通過Facebook平臺的數據服務來訪問有用的社會關系數據,為外部的Web應用、桌面操作系統應用和其他設備上的應用提供社會關系上下文。
<2>.應用可以通過一種名為FBML的數據驅動標記語言來實現顯示,在http://facebook.com的頁面上集成他們的應用體驗。
<3>.通過FBML所要求的架構改變,開發者可以使用Facebook平臺的cookie和Facebook JavaScript(FBJS),從而讓應用出現在http://facebook.com上所需的改動最小。
<4>.最后,應用可以獲得這些功能,同時不必犧牲隱私,也不必放棄對于Facebook為用戶數據和顯示提供的用戶體驗的期望。
(3).最后一點是最有趣的。Facebook平臺的架構并非一直是美麗的——它主要被看成是社會關系平臺領域的先行者。大多數的架構考慮是為了創建統一可用的社會關系上下文,它體現了這樣的陰陽關系:數據可獲得性和用戶隱私。
4.2 創建一個社會關系Web服務
<1>.回過頭來看一看像http://fettremansbooks.com這樣一個簡單的例子,我們就很清楚大多數因特網應用都會因為在數據顯示時添加社會關系上下文而受益。但是,我們會遇到一個實際的問題:這種數據的可獲得性。
<2>.實際問題:應用可以利用在Facebook上的用戶社會關系數據,但這種數據是不可訪問的。數據解決方案:通過一個外部可以訪問的Web服務來提供Facebook數據。 為Facebook架構添加了Facebook API,就開始通過Facebook平臺為外部應用和Facebook建立了關系,本質上為外部應用棧添加了Facebook數據。對于Facebook用戶,當他顯式的授權外部應用可以代表他獲得社會關系數據時,這種集成就開始了。
<3>.讓我們來看看Facebook的API如何支持這一點。首先,我們會簡單瀏覽一下Web服務包裝Facebook數據的技術,這是通過使用合適的元數據以及名為Thrift的靈活的代碼生成器生成的。開發者可以使用下一節中提到的這些技術,有效的創建各種Web服務,不論開發者手中的數據是公有的還是私有的。 但是請注意,Facebook的用戶并不認為他們的Facebook數據全部是公有的。所以在技術概述之后,我們會探討Facebook層面的隱私,這是通過平臺API中的主要認證方式來實現的,即用戶會話。
4.2.1 數據:創建一個XML Web服務
(1).為了能夠在一個示例應用中提供基本的社會關系上下文,我們已經建立了兩個遠程方法調用,即frids.get和users.getinfo。訪問這些數據的內部功能可能存在于Facebook代碼樹的某個庫中,為Facebook站點上的類似請求提供服務。
(2).創建一個簡單的Web服務,將通過HTTP的GET和POST輸入轉換成對內部棧的調用,以XML的格式輸出結果。在Facebook平臺中,目標方法的名稱以及它的參數是在HTTP請求中傳遞的,還包括一些與調用應用相關的證書(稱為“api key”),與用戶-應用對相關的證書(稱為“用戶會話key”),與請求實例本身相關的證書(稱為請求“簽名”)。要服務一個針對http://api.facebook.com的請求,其大致過程如下:
<1>.檢查傳遞的證書,驗證調用應用程序的身份,用戶當前在該應用中的授權,以及請求的可信度。
<2>.將進入的GET\POST請求解釋為帶有相應參數的方法調用。
<3>.對內部方法進行單次調用,將結果保存為內存中的數據結構。
<4>.將這些數據結構轉換成已知的輸出格式(如XML或JSON)并返回。 創建外部可使用的接口,難度主要在于第2步和第4步,為外部使用者提供這些數據接口的一致維護、同步和文檔是很重要的,手工打造一個代碼架構來確保這種一致性則是一項無人贊賞而又耗時的工作。另外,我們可能需要將這些數據提供給多種語言編寫的內部服務來使用,或者以不同的Web協議將結果提供給外部開發者,如XML\JSON或SOAP。 那么這里的優美解決方案,就是利用元數據來封裝數據類型和描述API的方法簽名。Facebook的工程師創建開源的跨語言進程間通信(IPC)系統,名為Thrift(http://developers.facebook.com/thrift)
(3).Thrift生成類似的代碼來聲明RPC函數調用、序列化成已知的輸出格式,并將內部的異常轉化成外部錯誤代碼。其他像XML-RPC或SOAP這樣的工具集也提供這樣一些好處,但可能需要更多的CPU和帶寬開銷。使用像Thrift這樣的漂亮工具有以下好處:
<1>.自動化類型同步
在user類型中添加“favorite_records”,或將uidz轉換成i64需要字所有使用或生成這些類型的方法中進行;
<2>.自動化綁定生成
所有讀寫類型的麻煩工作都不需要了,轉換函數用生成XML的RPC方法要求函數聲明、類型檢查和錯誤處理,這些都由Thrift自動完成;
<3>.自動化文檔
Thrift生成公開的XML Schema文檔,它將作為外界看到的無二義的文檔,通常比在“手冊”上看到的文檔要好得多。這種文檔也可以直接在一些外部工具中使用,生成客戶端的綁定。
<4>.跨語言同步 這個服務可以由外部的XML客戶端或JSON客戶端調用,內部是通過各種語言(PHP\JAVA\C++\Python\Ruby\C#等)寫的服務程序通過套接口來通信的。這要求基于元數據的代碼生成,這樣服務的設計就不必在每次小改動時花時間更新這些代碼。
4.2.2 簡單的Web服務認證握手
(1).一個簡單的認證策略讓我們能夠在尊重Facebook用戶的隱私觀點的前提下訪問這些數據。用戶對Facebook系統的數據有某種特定的視圖,這取決于用戶是誰、用戶的隱私設定,以及與也會有關系的人的隱私設定。用戶可以授權單個應用來繼承這一視圖。用戶通過某個應用可以看到的信息,是用戶通過Facebook可以看到的信息中有意義的一部分(但不會超出通過Facebook可以看到的信息)。
(2).Web服務的客戶端只要在每次請求時發送session_key,讓Web服務知道這代表的哪個用戶的請求執行。如果用戶(或Facebook)禁用了這個應用,或者他從未用過這個應用,安全檢查就會通不過,會返回一個錯誤。否則,外部應用站點會把這個會話鍵記入它自己的用戶記錄,或者放到該用戶的cookie中。 但是最開始如何得到這個會話鍵呢?在http://fettermansbooks.com應用代碼中的eatablish_facebook_session是一個占位符,為這個過程保留的。每個應用都有它自己特有的“應有鍵”(也稱為api_key),開始應用認證流程:
<1>.用戶通過一個已知的api_key重定向到Facebook登陸界面;
<2>.用戶在Facebook上輸入口令,對這個應用授權;
<3>.用戶帶著會話鍵和用戶ID重定向到已知的應用;
<4>.應用現在獲得了授權,可以代表用戶調用API方法(除非會話超時或被刪除)。
(3).有些應用不容易適應這種第二步“重定向”的方式?!白烂妗憋L格的應用、基于設備的應用(如手機應用),或瀏覽器內建的應用有時候也相當有用。在這種情況下,我們采用一種稍微不同的機制來使用第二次認證令牌。令牌是應用通過API請求得到的,在第一次登陸時傳遞給Facebook,然后在現場用戶認證之后,應用換到一個會話鍵和會話專有的一些秘密信息。
4.3 創建社會關系數據查詢服務
<1>.通過一個帶有用戶控制的認證握手的Web服務,我們已經將我們的內部庫擴展到外部世界。通過這個簡單的改變,Facebook的社會關系數據現在可以驅動其用戶決定認證的任何其他應用程序,通過普遍關注的社會關系上下文,在應用的數據中創建新的關系。隨著用戶漸漸了解這些數據交換的無縫性,使用這些平臺API的開發者知道這些數據集是很獨特的。開發者訪問自己的數據的模式與訪問Facebook數據的模式有著很大的不同。
<2>.實際問題:從Facebook平臺API獲取數據要比獲取內部數據的開銷大很多。 數據解決方案:類似內部數據采用的模式,實現外部數據訪問模式:一種查詢服務。 Facebook的解決方案稱為FQL,FQL很像SQL,但它將平臺數據轉換成字段和表,而不是簡單松散的定義為XML Schema中的對象。這讓開發者能夠在Facebook的數據上使用標準的數據查詢語義,這種方式可能與他們取得自己數據的方式一樣。同時,將計算推到平臺一端的好處與將操作通過SQL推到數據層的好處是相似的。在這兩種情況下,開發者有意識的避免了在應用邏輯中進行這種處理的代價。 FQL代表了基于Facebook的內部數據的另一項數據架構改進,是標準的黑盒Web服務的進步。但是首先,我們先來看一種容易而明顯的方法,它讓開發者能夠消除多次數據請求的來回開銷,同時我們也要說明為什么這是不夠的。
4.3.1 批量方法調用
<1>.對于負載問題最簡單的解決方案,就是類似于Facebook的batch.runAPI方法。這消除了多次通過HTTP棧對http://api.facebook.com進行調用的來回開銷,一批接受多個方法調用的輸入,一次返回輸出的多棵XML樹。
<2>.在Facebook平臺的PHP5客戶端庫中,end_batch實際上是向平臺服務器發起請求,取得所有結果,并針對每個結果更新引用的變量。這里我們從一次用戶會話中批量獲取了用戶數據。通常,人們用批量查詢機制將許多設置操作歸為一組,如大量的Facebook個人描述更新,或大量突發的用戶通知。
<3>.這些批量操作很有效,但這也揭示了這種批量操作的主要問題。問題是,每次調用必須與其他調用的結果無關。對多個不同用戶的批量操作通常具備這種特點,但有一種常見的情況仍然不能處理,即使用一次調用的結果作為下次調用的輸入。
4.3.2 FQL
(1).FQL是一種簡單的查詢語言,它包裝了Facebook的內部數據。輸出的格式通常與Facebook平臺API的輸出格式一樣,但輸入超出了簡單的RPC庫的模型,變成了SQL的查詢模型:命名的表和字段,包含已知的關系。像SQL一樣,這種技術添加了選擇實例或范圍的能力,從數據行中選擇字段子集的能力,并通過嵌套查詢將更多的工作推到數據服務器端,避免了通過RPC棧進行多次調用。
(2).我們在概念上將users_getInfo引用的數據是為一個表,它基于一個索引(uid),包含一些可選擇的字段。如果正確的擴展,這種新的語法可以支持一些新的數據訪問能力:
<1>.限定范圍查詢;
<2>.嵌套查詢;
<3>.結果集大小限制和排序。
(3).FQL架構
<1>.開發者通過fql_query API來調用FQL。問題的要點是在FQL的命名“表”和“字段”中,統一外部API的命名“對象”和“屬性”。我們仍然繼承了標準API的流程:通過內部方法取得數據,應用跟這個方法的API調用相關的規則,然后根據第4.2.1節介紹的Thrift系統,轉換到輸出。對于每個數據讀取API方法,在FQL中都有一個對應的“表”,代表了這次查詢背后的數據抽象。
<2>.例如,API方法users_getInfo,它提供給定用戶ID的姓名、照片、書籍和當前位置等字段,在FQL中它就表現為用戶表和對應的字段。fql_query的輸出實際上也符合標準API的輸出(如果修改XSD來允許省略對象小的字段),所以在用戶表上調用fql_query返回的輸出與相應的users_getInfo調用是等價的。事實上,像user_getInfo這樣的調用在Facebook的服務器端通常是實現為FQL調用的。
<3>.FQL還有其他一些精妙之處,但這個總體流程說明了已有的內部數據訪問和隱私規則實現與全新的查詢模型的結合。這讓開發者能夠更快的處理它的請求,能夠以比API更好的粒度來訪問數據,同時又保持了SQL類似的語法。
4.4 創建一個社會關系Web門戶:FBML
(1).前面討論的服務讓外部的應用棧能夠在它們的系統中包含社會關系平臺的數據,這是很大的進步。這些數據架構實現了讓社會關系平臺數據更開放的承諾:外部應用和數據平臺的共同用戶可以共享信息,每個新的社會關系應用就不需要一個新的社會關系網絡。但是,即使有了這些新的能力,這些應用還是不能享受Facebook這樣的社會關系網站的全部強大的功能。應用還需要讓許多用戶發現,才會變得有價值。而且,并不是所有支持社會關系平臺的內部數據都可以提供給這些外部的應用棧。
(2).實際問題:對于社會關系應用來說,要獲得引人注目的關鍵性用戶數,支持它的社會關系網絡上的用戶必須要能注意到其他用戶在利用這些應用進行交互。這意味著應用與社會關系網站更深層次上的集成。
這個問題在早期的軟件中就存在:我們難以讓數據、產品或系統得到廣泛使用。缺少用戶成功Web 2.0空間中特別值得一提的困難,因為如果沒用戶使用而且(特別是)生成內容,我們的系統什么時候才有用呢? Facebook支持大量的用戶,他們對在社會聯系之間共享信息感興趣,而且Facebook的特點就是把應用的內容和它自己的內容等同視之,應用都需要在Facebook站點上有獨特的顯示展現。
(3).實際問題:外部應用不能夠使用Facebook沒有通過Web服務暴露出來的那些核心數據元素。在Facebook提供網站(http://facebook.com)的內容時,Facebook為它的用戶提供了大量的數據。隱私信息本身就是一個很好的例子,不能被Facebook站點的用戶顯式的看到。但是強制實現Facebook用戶的這種隱私設置是所有良好集成的應用的特點,也是對社會關系系統上用戶期望的支持。Facebook為了保護用戶的隱私,不能通過數據服務將這些數據開放出來。開發者怎樣才能利用這些數據呢? 對這些問題的最優雅解決方案就是結合Facebook的數據和外部應用的數據、邏輯和顯示,同時讓用戶在一個受信任的環境下操作。
數據解決方案:開發者通過一種數據驅動的標記語言,在社會關系站點上創建應用執行和顯示的內容,與Facebook交互。
(4).我們必須同時記得我們的資產和約束。一方面,我們有一個訪問頻率很高的社會關系系統,讓用戶能發現外部的內容,并有大量的社會關系數據來增強這種社會關系應用。另一方面,請求需要從社會關系站點(Facebook)上發起,將應用作為服務來使用,然后將內容渲染成HTML、JavaScript和CSS,并且不違反Facebook用戶的隱私或期望。
4.4.1 Facebook上的應用:直接渲染HTML、CSS和JS
<1>.對http://apps.facebook.com/fettermansbooks/...的請求于是簡單的取出應用服務器上的HTML\JS和CSS等內容,并在Facebook上的頁面主內容區域進行顯示。這基本上是將外部站點作為一個HTML Web服務來渲染的。
<2>.這對應用的n層模型進行了重要改變。以前,應用棧會通過數據服務來使用Facebook的內容,這個數據服務是直接服務于對http://fettermansbooks.com的請求的?,F在,應用在它的Web根下維護了一個樹形結構,它自己提供HTML服務。Facebook通過在線請求這個新應用服務(該服務又可能用到Facebook的數據服務)而取得內容,將它包裝成一般的Facebook站點導航元素,顯示給用戶。
<3>.但是如果Facebook直接在它的頁面中渲染一個應用的HTML、JavaSacript或CSS,這就會允許應用完全違反用戶對http://facebook.com上受控體驗的期望,讓站點和用戶暴露在各種安全攻擊下。允許外部用戶直接訂制標記語言和腳本幾乎從來都不是好主意。實際上,代碼或腳本注入通常是攻擊者的目標,所以這并不是一個很好的特征。而且沒有新數據!盡管這為應用棧的改變奠定了基礎,但這個解決方案沒有完全解決前面的兩個實際問題。
4.4.2 Facebook上的應用:iframe
<1>.還有一種更安全的顯示應用內容的方法,可以顯示另一個站點的可視化上下文和界面流轉,這種方法已包含在瀏覽器中,即irame。為了復用前一節中提到的映射,對http://apps.facebook.com/fettermansbooks/PATH?QUERY_STRING的請求將導致輸出這樣的HTML:<iframe src="http://fettermansbooks.com/fbapp/PATH?GET_STRIGN"></iframe>這個URL的內容將顯示在Facebook頁面的一個幀中,在它自己的沙盒環境中可以包含任何類型的Web技術:HTML\JS\AJAX\Flsh等。
<2>.這實際上是讓瀏覽器成為請求代理者,而不是由Facebook作為請求代理者。這比前一節中的模型有改進,瀏覽器也維護所得頁面中其他元素的安全性,所以開發者可以在這個幀中隨意創建他們想要的用戶體驗。對于某些應用,如果開發者希望花最小的代價將他們的代碼從他們的站點移到平臺上,那么iframe的方式也是有意義的。實際上,Facebook繼續支持完整頁面生成的iframe模型。雖然基于iframe的請求流程可以確保安全,但除了API服務暴露出來的數據之外,這些開發者并不能利用其他的新數據。
4.4.3 Facebook上的應用:FBML是數據驅動的執行標記語言
(1).HTML的解決方案采用了直觀的方法,將應用本身變成Web服務,將觸點帶回到Facebook上顯示。iframe方式的好處在于將開發者的應用內容放在一個獨立的(安全的)執行沙盒中。最佳解決方案將保留“應用即服務”的模型和iframe的安全和可信,同時又讓開發者能夠使用更多的社會關系數據。 問題是,為了讓社會關系應用提供特定的用戶體驗,開發者必須通過他們自己的應用棧來提供數據、邏輯和展示。但是,生成這些輸出必須用到那些不能離開Facebook的用戶數據。
(2).解決方案:不是發回HTML,而是一種特定的標記語言,其中定義了足夠的標記來表現應用的邏輯和顯示,也包含對受保護數據的請求,完全讓Facebook在受信任的服務器環境中渲染它!這就是FBML的前提。 在這個流程中,對http://apps.facebook.com的請求同樣被轉換成對應的請求,應用棧會使用Facebook的數據服務。但是,開發者不會讓應用返回HTML,而是返回FBML。
FBML中包含了許多HTML元素,而且添加了Facebook特別定義的標簽,FBML解釋器將這段標記語言轉換成它自己的數據、執行和顯示實例,生成應用頁面。用戶就會收到一個頁面,其中包含了Facebook頁面的一般Web元素,而且也包含了應用的數據、邏輯和觀感,它能在技術上確保Facebook強制實現其隱私理念和良好的用戶體驗元素。
(3).FBML是XML的一個特例,它包含了許多熟悉的HTML標簽,增加了在Facebook上顯示的平臺專有的標簽。FBML同樣體現了FQL的高級模式:修改已知的標準(HTML,對FQL來說就是SQL),將執行和決定延遲到Facebook平臺服務器上進行。FBML解釋器讓開發者通過FBML數據,自己能夠控制在Facebook服務器上執行的邏輯和顯示。這是數據處于執行中心的絕妙例子:FBML只是聲明式的執行,而不是必須服從的控制流。 FBML是一個XML實例,所以它由標簽、屬性和內容組成。標簽可以在概念上分成以下幾類:
<1>.直接的HTML標簽
如果FBML服務返回標簽<p/>,Facebook將在輸出頁上直接渲染為<p/>。作為Web展現的基石,大多數HTML標簽都是支持的,少數違反Facebook層面的信任或設計期望的標簽除外。
<2>.數據顯示標簽
這是體現數據威力的地方。假定個人簡歷照片不能轉到其他站點。通過指定<fb:profile-pic uid="8055">,開發者就可以在他們的應用中顯示更多的Facebook用戶信息,同時不要求用戶完全信任開發者,將這部分信息交給開發者處理。請注意,即使信息受到了保護,這些內容也不會返回到應用棧中,只是顯示給用戶看。在容器端執行使這些數據可以查看,但不要求將它們交給應用程序。
<3>.數據執行標簽
作為使用隱藏數據的一個更好的例子,用戶的隱私限制只能通過內部的can_see方法來訪問,它是應用體驗的一個重要部分,但不能通過數據服務從外面進行訪問。利用<fb:-if-can-see>標簽和其他類似的標簽,應用可以通過屬性來指定一個目標用戶,這樣只有當查看者能夠看到目標用戶的特定內容時,那些子元素才會渲染出來。
因此,隱私數據本身不會暴露給應用,同時應擁有能滿足強制實現的隱私設置。 從這個角度來說,FBML是一個受信任的聲明式執行環境,與C或PHP這樣的必須服從的執行環境不同。嚴格來說,FBML不像這些語言那樣是“圖靈完備”的。像HTML一樣,除了樹狀遍歷所隱含的狀態外,在執行時不保存任何狀態。但是,通過讓受信任系統中的用戶獲得數據,FBML提供了大量功能,這些功能正是大多數開發者希望提供給他們的用戶的。FBML實際上有助于定義執行應用的邏輯和顯示,同時又讓應用可以在應用服務器上顯示獨特的內容。
<4>.只面向設計的標簽
Facebook因其設計標準而受到稱贊,許多開發者都選擇以某種方式復用Facebook的設計元素,保持Facebook的觀感。通常,他們是通過利用http://facebook.com的JavaScript和CSS來實現的,但FBML提供了類似“設計宏”的庫,以更可控的方式來滿足這種需求。
<5>.替代HTML標簽
HTML造成了一些信任風險,但沒有暴露數據,所以FBML中的替換標簽只是修改或限制一組參數,如Flash自動播放。這不是所有顯示平臺都嚴格要求的,它們只是強制應用滿足容器站點的默認顯示行為。但是,隨著多個應用發展成為一個生態系統,它們都反映容器站點的觀感,這種修改就變得很重要了。
<6>.“功能包”標簽
某些Facebook FBML標簽包含了整套的常見Facebook應用功能。<fb:friend-selector>創建了類型的前置的朋友選擇器軟件包,常見于許多Facebook頁面,包括Facebook數據(朋友、主要網絡)、CSS樣式和針對鍵盤動作的JavaScript。像這樣的標簽讓容器站點可以推廣某些設計模式和應用間的公有元素,也讓開發者能夠快速實現他們想要的功能。
4.4.4 FBML架構
(1).將開發者提供的FBML翻譯成顯示在http://facebook.com上的HTML,需要一些技術和概念綜合作用:將輸入字符串解析成一棵句法樹,將這棵樹中的標簽轉換成內部方法調用,應用FBML語法規則,保持容器站點的約束。像FQL一樣,這里將關注點主要放在FBML與平臺數據的交互上,對其他的技術則不作詳細探討。FBML處理了一個復雜的問題,FBML的全部實現細節是相當多的——這里省略的內容包括FBML的錯誤日志、為后來的渲染事先緩存內容的能力、表單提交結果的安全性簽名等。
(2).解析FBML的底層問題。在繼承了瀏覽器的某些角色的同時,Facebook也繼承了它的一些問題。為了方便開發者,不要求提供的輸入可以通過schema驗證,甚至不要求是結構良好的XML——不封閉的HTML標簽,打破了輸入必須作為真正的XML進行解析的假定。因為這一點,需要一種方法將輸入的FBML字符串先轉換成結構良好的句法樹,包含標簽、屬性和內容。為了做到這一點,采用了一個開放源代碼瀏覽器的一些代碼,假定在接收到FBML并經過這樣的處理流程后,得到名為FBMLNode的樹狀結構,它讓能夠查詢生成的句法樹中任何節點的標簽、屬性鍵值對和原是內容,并能夠遞歸查詢子元素。
(3).從最高的層面上看,會注意到FBML出現在Facebook站點的所有地方:應用“畫布”頁面、新聞信號源的故事內容、個人簡介等等。每種上下文中或每種“風味”的FBML都定義了對輸入的約束,因為FBML維護數據隱私的方式與API類似,所以執行上下文中必須包含查看用戶ID和生成核對內容的應用ID。所以在真正開始使用FBML之前,先看看環境的規則,它是由FBMLFlavor類來封裝的。
這種風味類的設計很簡單:它包含了隱私上下文(用戶和應用),實現了檢查方法,為稍后將展示的FBMLImplementation類中包含的豐富邏輯建立了規則。與平臺API的實現層很像,這個實現類為服務提供了實際的邏輯數據訪問,其他的代碼為這些方法提供了訪問入口。每個Facebook特有的標簽,將有一個對應的實現方法,每個實現方法都接收一個來自FBML解析器的FBMLNode,以字符串的方式返回輸出的HTML。下面是具體實現的程序清單:
<1>.在FBML中實現直接的HTML標簽
例如<img>標簽的內部FBML實現,是將圖像標簽的實現包含更多的邏輯,有時候需要將圖像源的URL重寫到Facebook服務器上圖像緩存的URL。這體現了FBML的強大:應用??梢苑祷嘏cHTML非常相似的標記語言,支持它自己的站點,而Facebook可以通過純技術的手段強制實現平臺所要求的行為。
<2>.在FBML中實現數據顯示標簽
通過FBML使用Facebook數據的例子。<fb:profile-pic>用到了uid、size和title屬性,將它們結合起來,根據內部數據產生HTML輸出,并符合Facebook的標準。在這個例子中,輸出是指定用戶名的個人簡單照片,鏈接到用戶的個人間接頁面,只在當查看能看到這部分內容時才顯示。這個功能也存在于FBMLImplementtation類中。
<3>.FBML中的數據執行標簽
FBML解析的遞歸本質使得<fb:if-can-see>標簽就像是標準的必須服從的控制流中的if語句一樣,它是FBML實際控制執行的一個例子。這是FBML實現類中的另一個方法。如果對“觀察者-目標”通過了can-see檢查,引擎就會遞歸的渲染<fb:if-can-see>節點的子節點。否則,就會渲染可選標簽<fb:else>子節點下的內容。請注意fb_if_can_see直接訪問<fb:else>子節點的方式;如果<fb:else>出現在這樣的一個“if風格”的FBML標簽之外,標簽和它的子標簽就不會返回任何內容。所以,FBML不僅僅是一個簡單的轉換式例程,它會注意到文檔的結構,因此可以包含條件控制流的元素。
<4>.結合在一起
前面討論的每個功能,都需要注冊為一個回調,在解析輸入的FBML時使用。在Facebook這個“黑盒”解析器是用C寫的PHP擴展,每個回調都存在于PHP樹中。要完成這種高層控制流,我們必須向FBML解析引擎聲明這些標簽。 FBML利用回調擴展了瀏覽器的解析技術,包裝了由Facebook創建和管理的數據、執行和展現宏。這個簡單的思想實現了應用的完全集成,支持使用通過API暴露出來的內部數據,同時保持安全性方面的用戶體驗。FBML本身幾乎就是一種編程語言,它也是充分發展后的數據:外部提供的聲明式執行,安全的控制了Facebook上的數據、執行和顯示。
4.5 系統的支持功能
<1>.現在,開發者創建的軟件運行在Facebook的服務之上,不僅是結合了界面組件,而是全部的應用。在這個過程中,創造了一個社會關系網絡應用的完全不同的概念。從一個典型的Web應用的獨立數據、邏輯和顯示的標準設置開始,不考慮所有社會關系數據,只是讓用戶可以確信能夠做出貢獻?,F在,取得從分的進展,應用使用了Facebook的社會關系數據服務,同時它自己又成為一個FBML服務,完全集成到容器站點之中。
<2>.Facebook數據也獲得了長足的發展,不再僅僅是第一章討論的內部庫。但是,仍有一些重要的、常見的Web使用場景和技術,目前平臺未支持。通過將應用變成一個返回FBML的服務,而不是直接由瀏覽器解讀的HTML\CSS\JS,我們接觸到了關于現代Web應用的一些重要假定。
4.5.1 平臺cookie
<1>.應用的新Web架構排除了瀏覽器內建的一些技術,許多Web應用??赡芤蕾囉谶@些技術??赡芷渲凶钪匾囊稽c是,過去瀏覽器用于保存用戶與應用棧交互信息的cookie不再刻意得到了,因為應用的目標消費者不再是瀏覽器,而是Facebook平臺。cookie信息屬于該應用領域所提供的用戶體驗。
<2>.解決方案是什么?讓Facebook具有瀏覽器的職責,在Facebook自己的存儲庫中復制這種cookie功能。如果應用的FBML服務送回請求頭,試圖設置瀏覽器cookie,Facebook就保存這個cookie信息,以(user,application_id)對為主鍵。Facebook然后“重新創建”這些cookie,就像用戶向這個應用棧發出后續請求時瀏覽器所做的一樣。注意當用戶決定在這個應用提供的HTML棧上導航時,這種信息是不能使用的。另一方面,它可以有效的分離用戶的Facebook上的應用體驗和在應用的HTML站點上的應用體驗。
4.5.2 FBJS
(1).當應用棧作為一個FBML服務被使用,而不是直接由用戶的瀏覽器來使用,Facebook就沒有機會執行瀏覽器端的腳本。直接返回未修改過的開發者提供的內容可以解決這個問題,但它違背了Facebook在顯示體驗上所加的約束。例如,當價值用戶的簡介頁面時,Facebook不希望在加載事件上觸發一個彈出窗口。但是,限制所有的JavaScript會排除許多有用的功能,如AJAX或在不重新加載的情況下動態操作頁面的內容。
(2).相反,FBML在解釋開發者提供的<script>樹和其他頁面元素的內容時會考慮到這些約束。在此之上,Facebook提供了一些JS庫,讓這些場景容易實現,同時又受到控制。這些修改共同構成了Facebook平臺JS仿真套件,稱為FBJS,它通過以下幾點,讓應用既動態又安全:
<1>.重寫FBML屬性,確保實現虛擬文檔范圍;
<2>.延遲激活腳本內容,直到用戶在頁面或元素上發起動作時;
<3>.提供一些Facebook庫,以受控的方式來實現常見的腳本使用場景。 很清楚,不是所有的實現自有平臺的容器站點都需要這些修改,但FBJS向我們展示了幾種解決方案,這樣的新Web架構需要這些解決方案來繞過一些困難。這里只展示解決方案的一般思想,與FBML和擴展的專有JS庫進行融合。
(3).JS通常可以訪問包含它的文檔的整個文檔對象模型(DOM)樹。但是在平臺畫布頁面中,Facebook包含了許多它自己的元素,開發者不允許對它們進行修改。解決方案是什么?在用戶提供的HTML元素和JS符號之前加上前綴,即應用的ID。通過這種方式,在開發者的JS中如果試圖調用不允許調用的alert()函數,就會調用未定義的函數,并且只有開發者自己提供的那部分文檔的HTML可以被document.getElementById這樣的JS代碼訪問。
(4).對于FBML和FBJS中的NOTE注釋的解釋:
<1>.NOTE1
Facebook需要包含它自己的特殊JS,包括fbjs_sandbox的定義,目的是渲染開發者的腳本。
<2>.NOTE2
FBM會重寫初始化流程中的屬性,變成Facebook特有的功能,這實際上是FBJS的一部分。所以這里的onclick會激活這個頁面的其他元素,這些元素在用戶執行這個動作之前是非激活的。
<3>.NOTE3
注意在HTML和腳本中的元素如何加上了該應用的應用ID作為前綴的。這意味著開發者對alert()的調用將變成對app12345_alert()的調用。如果Facebook的后臺JS在這個上下文中允許這個方法,它將最終轉向執行alert()。如果不允許,這將是未定義的調用。類似的,這種加前綴的方式時機尚未DOM樹提供了命名空間忙所以對該文檔某些部分的改變只限于開發者定義的那些部分。類似的沙盒技術也允許開發者提供限制范圍的CSS。
<4>.NOTE4
Facebook提供了一些專門的JS對象,如AJAX和Dialog,目的是支持常見的使用場景。例如,通過AJAX()對象發出的請求實際上能獲得FBML作為結果,所以它們被重定向到Facebook域的一個代理上,在這里Facebook完全在線的FBML到HTML的轉換。 支持FBJS需要對FBML進行改動、專門的JS和AJAX代理這樣的服務器端組件,才能夠繞過應用Web架構的一些限制,但結果是很強大的。開發者因此可以享受絕大多數的JS功能,而且平臺確保了應用內容提供了用戶在Facebook上期望的受控體驗,這完全是通過技術手段來實現的。
4.5.3 服務改進小結
解決了新的n層社會關系應用的概念帶來的剩下一些問題之后,我們又改進了服務架構,添加了cookie和FBJS等項。隨著開發者的社會關系應用越來越成為Facebook使用的一項集成服務,而不是由瀏覽器使用的外部站點,我們已經重新創建或重新設計了瀏覽器的某些功能(通過平臺的cookie、FBJS等)。在試圖改變或重建“應用”的概念時,這是必須的兩個重要修改的例子。Facebook平臺包括類似的其他一些架構上的巧妙設計,這里只是簡單介紹,還有一些包括數據存儲API和瀏覽器端Web服務客戶端。
4.6 總結
Facebook的用戶貢獻的社會關系有效的提高了http://facebook.com上幾乎所有頁面的效果。而且,這種數據非常通用,所以當它與外部開發者的應用棧結合在一起時,它的最佳使用就出現了,這都是通過Facebook平臺的Web服務、數據查詢服務和FBML等技術來實現的。 從取得用戶的朋友或簡介信息的簡單內容API開始,有介紹了一些全部改進展示了如何協調不斷擴展的數據訪問方法和容器網站的預期,特別是對數據隱私和站點體驗集成方面的要求。每次對數據架構的新改動都發現了Web架構的一些新問題,又通過對數據 訪問模式的更強改進來解決這些問題。
雖然關注重點在Facebook的社會關系數據平臺的應用的潛力和約束上,但像這樣的新型數據服務不一定局限于社會關系信息。隨著用戶貢獻和使用的信息越來越多,這些信息在許多容器站點上都很有用,各式各樣的平臺提供者可以應用Facebook平臺特有的數據和Web架構背后的這些思想,并從中獲益。
?
轉載于:https://www.cnblogs.com/SanMaoSpace/archive/2012/12/28/2838021.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: Matlab中plot基本用法
- 下一篇: VIM编辑器使用技巧