架构之美 精选版
本文轉載自:
?
架構之美
【作 者】 Till Adam
?
精選版
Diomidis Spinellis 等著
王海鵬 等譯
頂級業界專家揭密軟件設計之美
免費在線版本
(非印刷免費在線版)
登錄China-Pub網站購買此書完整版
了解本書更多信息請登錄本書的官方網站
InfoQ 中文站出品
本書由 InfoQ 中文站免費發放,如果您從其他渠道獲取本書,請注冊InfoQ 中文站以支持作
者和出版商,并免費下載更多InfoQ 企業軟件開發系列圖書。
本書主頁為
http://infoq.com/cn/minibooks/beautiful-architecture
注:封面圖片選自 http://www.flickr.com/photos/ladymolly/444785397/
歡迎免費下載
商務合作:sales@cn.infoq.com 讀者反饋/內容提供:editors@cn.infoq.com
目錄
推薦序 ........................................................................................................................................i
譯者序.......................................................................................................................................v
作譯者簡介.............................................................................................................................. vii
第1 章架構概述.......................................................................................................................1
1.1 簡介.................................................................................................................................1
1.2 創建軟件架構..................................................................................................................7
1.3 架構結構........................................................................................................................11
1.4 好的架構........................................................................................................................15
1.5 美麗的架構....................................................................................................................16
致謝......................................................................................................................................18
參考文獻...............................................................................................................................19
第2 章兩個系統的故事:現代軟件神話.............................................................................21
2.1 混亂大都市....................................................................................................................22
2.2 設計之城........................................................................................................................28
2.3 說明什么問題................................................................................................................35
2.4 輪到你了........................................................................................................................36
參考文獻...............................................................................................................................36
第3 章伸縮性架構設計.........................................................................................................37
3.1 簡介...............................................................................................................................37
3.2 背景...............................................................................................................................38
3.3 架構...............................................................................................................................42
3.4 關于架構的思考............................................................................................................47
第4 章數據增長:Facebook 平臺的架構...........................................................................52
4.1 簡介...............................................................................................................................52
4.2 創建一個社會關系Web 服務.......................................................................................57
4.3 創建社區關系數據查詢服務........................................................................................64
4.4 創建一個社會關系Web 門戶:FBML........................................................................72
4.5 系統的支持功能............................................................................................................85
4.6 總結...............................................................................................................................90
推薦序一
如何看到一滴水的美麗
支付寶(中國)公司業務架構師
《大道至簡》作者
周愛民(aimingoo)
【一】
架構是一個過程,而非一個結果。
【二】
在大多數人的談論中,架構是一個目標產物,而作為架構師的責任就是去生產它。所以
無論如何,架構是可以“做”出來的,而且也應該有一些“做”的方法、技術、技巧。
有人問過我:架構的最主要產出是什么?我的答案是:圖。這里面有兩層含義:一層含
義是如同建筑師描繪的藍圖一樣,用于引導實施者;另一層含義是架構師頭腦中清晰的
目標系統。如果架構師頭腦中沒有系統清晰的圖像,他是沒有辦法把它畫出來的。
【三】
畫家畫的無非是物我。畫物的畫家,最終畫的還是我見。所以,畫家的筆最終描繪的是
他自己心里的映像。
【四】
藝術是不可能被“生產”出來的,生產出來的,叫“藝術品”。
【五】
架構這個過程,是架構師洞見系統內在結構、規律、原則和邏輯的過程。真正的架構師
是可以將自己放在系統中去的(例如作為系統中的任何一個角色),只有清晰地理解系
統,才能簡潔地描述它。而當架構師拿出了他所描述的“作品”的時候,架構這一過程
就已經結束了。
【六】
一滴水滴落的過程中,有多少個形態的變化?
InfoQ企業軟件開發叢書《架構之美》精選版
推薦序二
架構的架構
北京無限訊奇信息技術有限公司產品技術高級總監
黃冬
從編輯手里拿到厚厚的《架構之美》譯稿時,恰巧是我剛剛講完一場消息系統架構的講
座之后。而正是在昨天,一位想要創業的朋友跟我說要尋找一位懂得“架構”的高人與
他一起創業。要知道與代碼不同的是,“虛幻”的架構常常讓人認為其有很多玄妙之處,
只因它大多難以落在紙上。特別是與很多大師談及架構時,經常落入他們的一些“陷阱”,
并往往為自己達不到大師的精明與技巧而嘆息。殊不知,被我們所津津樂道的這些架構,
是他們在日常工作里經歷了大量的錯誤、重復的嘗試、無數的代碼、長久的考驗所積淀
下來的只言片語。
本書將數十人的經歷與只言片語,經過深思熟慮后抽象出規律,使之可以不斷復用。而
另一方面,又將架構的過程娓娓道來,嘗試讓讀者思考架構的過程與思路。在這里,更
多的過程與思考被展現出來,更多的原因與為什么讓我們了解。
這本書里展現了很多絢麗的故事,猶如士兵閱讀將軍的傳記一樣,閱讀本書將會讓你更
鼓起勇氣追尋大師們的腳步,但永遠要記住,每一滴汗水才真正是你成長路上的每一個
記號,要在自己的工作里更深地去理解每一處不同,架構出屬于自己的系統。
感謝譯者和出版者為我們帶來這樣一本傳奇的架構故事書。
InfoQ企業軟件開發叢書《架構之美》精選版
推薦序三
美麗架構的含義
騰訊R&D研發總監(Tencent Director Of R&D Development)
資深技術專家(Senior Technology Expert)
王速瑜
古人形容美女之美:“??增之一分則太長,減之一分則太短??”,深刻地揭示了“恰
到好處”的美麗含義。當我拿到《架構之美》書稿時,我發現美麗的含義如此相似。
美麗至簡。美麗的架構應盡可能簡單,但不要過于簡單。書中通過多種例子表達了這個
最基本的道理。我見過很多大型的軟件架構,從大型的電信網絡管理系統,到大規模應
用的互聯網架構,以及企業級的ERP軟件,系統總是遵循從無到有,從簡單到復雜,再
到簡單這樣的過程。最終,支撐這些大型系統穩定可靠運行的就是這個最基本的道理。
美麗的架構應盡可能精益,并且是演進式發展的。當你架構一個億萬人同時在線的大規
模網站系統的時候,你無法從一開始就提供最完善的解決方案,它應該是隨著用戶的增
長而可擴展的。精益的思想讓你避免了過度設計,也使架構不斷演進,趨于完美。書中
從企業級應用架構、用戶級應用架構等多個角度提供了相應的解決方案,對于架構師無
不是一頓美味的大餐。
深夜看完這本書稿后,我發現,架構之美并不簡單,它沒有定法。但是,它將為架構師
們提供一把進入“美麗架構藝術館”大門的鑰匙。拿起它,您將會開啟這扇大門!
InfoQ企業軟件開發叢書《架構之美》精選版
推薦序四
美麗架構之道
《構建高性能Web站點》作者
Web架構實踐者
郭欣
我無法給架構下一個簡單的定義,因為任何定義都會束縛你對架構的無限想象。不可否
認,架構師早已出現在人類幾千年前的各項生產活動中,比如建筑、音樂。而在計算機
軟件及Web領域,架構的設計直接影響著系統的生產,比如開發過程和效率、代碼和組
件復用性等,同時也影響著系統的可用性、可伸縮性、性能、容量可預測性等。
本書更加關注架構之美。美麗的架構同樣無法定義,可它卻一定是自然的、簡單的、可
復用的、人文的,甚至是外行人也可以細細品味其思想的。當我看到超市的多個收銀臺
排滿長隊時,便想到服務器并發處理性能和容量;當我看到十字路口的車輛等待轉彎時,
便想到它通過緩存思想來提高交通吞吐率。
那么如何設計出美麗的架構呢?從代碼邏輯到物理網絡,從單機到分布式,無數的技術
可供架構師選擇,如分層、組件化、服務化、標準化、緩存、分離、隊列、復制、冗余、
代理等,不過它們仍然只是“術”的范疇,而何時何處如何恰到好處地使用它們才是
“道”的范疇,比如頓悟變化的道理,在博弈中尋找平衡,以系統化的角度來分析問題,
尋找相對與絕對的奧秘、開放的心態??
然而,這個領域實在是太年輕了,我們需要更多的例子和經驗,本書將讓你大開眼界!
InfoQ企業軟件開發叢書《架構之美》精選版
譯者序
架構與美
王海鵬
人們在生活和工作中發現美并創造美,軟件開發和架構設計也不例外。
架構之美體現了關注點的分離與結合。在軟件設計中,設計師需要考慮多方面的關注點。
漂亮的架構設計讓這些關注點盡可能分離,然后以最簡單的機制結合在一起,從而得到
高內聚、低耦合的系統。例如在Darkstar項目中,架構師們考慮的重點就是如何將多人
在線游戲的游戲邏輯與系統的可伸縮性分離開來,讓游戲的開發者只要遵守少量的規則,
就能夠像編寫單機游戲一樣編寫大規模多人在線游戲。又如REST架構風格,體現了對
資源命名、請求處理和資源物理表現形式的關注點分離。資源的名稱與請求資源時服務
器的處理方式無關,請求者無需知道服務器端采取的技術,并且請求者本來就不關心服
務器端的處理技術。資源的物理表示形式可以通過內容協商來決定,使系統可以支持多
種物理表示形式,并可以方便地擴展。
架構之美注重表達的簡潔性?!癉on’t Repeat Yourself”,好的架構致力于消除各種類型
的信息重復。從結構化程序設計中的子程序和函數,到面向對象程序設計中的繼承,無
不體現了對表達簡潔性的特殊偏愛。在敏捷方法學中,消除重復則是重構的主要目的之
一。愛因斯坦說:“讓它盡可能簡單,但不要過于簡單?!蔽覀冃枰紤]所有必須考慮的
關注點,然后用簡潔漂亮的架構體現我們的關注。同時,簡潔的架構之美也降低了軟件
的總體成本,從這個意義上說,“簡潔性”又可以稱為“經濟性”。
架構之美需要解決實際問題,它既是藝術,也是生活。軟件像建筑一樣,它的美不能脫
離它的實用價值。Bjarne Stroustrup說,人類文明運行于軟件之上。每一個軟件都有自
己的架構,這些架構有的很美,有的不太美。從藝術的角度來說,美是創造矛盾并解決
矛盾。架構的多關注點和表達簡潔性就是一種矛盾,美麗的架構提供了這一矛盾的解決
方法,讓我們的內心產生一種愉快的感覺。
InfoQ企業軟件開發叢書《架構之美》精選版
架構之美需要經過專業的學習才能更好地欣賞和創造。和所有的藝術之美一樣,不是說
不經過專業學習就不能欣賞,但是經過了專業的學習,就能更好地欣賞這種美的種種精
妙之處。如果想要創造出這種美,那就必然要經過長期的專業學習。
架構之美經過時間打磨。像Facebook面向數據的Web服務、FQL和FML架構,是在對應不
同實際需求的過程中逐漸發展起來。在應用程序架構形成的過程中,設計者不斷面對新
的關注點需求,不斷對已有的架構進行修改,并發展出新的架構組件。這就是所謂的
“演進式架構”。只有變化是永恒不變的。在架構設計初期,設計者會將一些關注點有意
推遲到將來考慮,例如持久和并發。對于這些暫不考慮的關注點,設計者對它們的實現
方式盡可能不做任何假定,從而保留更多的可能性,讓不同關注點之間的耦合盡可能小。
架構之美沒有定法。雖然有一些法則可供我們參考,卻沒有非如此不可的。《金剛經》
云:“一切賢圣,皆以無為法而有差別?!?br /> 參加本書翻譯工作的人員還有蔡黃輝、徐鋒、王海燕、李國安、周建鳴、范俊、張海洲、
謝偉奇、林冀、錢立強、甘莉萍。
在這本書的翻譯過程中,我受益良多,因此鄭重地向大家推薦它。
xii 譯者序
InfoQ企業軟件開發叢書《架構之美》精選版
作譯者簡介
作者簡介
Till Adam在年輕時學習了哲學、比較文學、美國研究和音樂學,職業是音樂人。由于
沒有發財和出名,他轉而攻讀科學碩士,學習了數學、計算機科學和商業。多年從事自
由軟件的經歷(特別是對KDE的貢獻)教會了他編程,也為他帶來了在Klar?lvdalens
Datakonsult AB工作的機會,目前他在該公司負責協調KDE的開發和其他與自由軟件相
關的活動。他和他的妻子、女兒住在德國柏林。
Jim Blandy在1990年至1993年間為自由軟件基金會維護GNU Emacs,和Richard Stallman
一起發布了Emacs的第19個版本。他是Subversion版本控制系統的最初設計者之一。他也
是CVS版本控制系統、GNU調試器(GDB)、Guile擴展語言庫和一個編輯基因序列的
Emacs程序的貢獻者。他現在為Mozilla公司工作,工作內容是SpiderMonkey,即Mozilla
的Javascript編程語言的實現。Jim和他的妻子、兩個女兒住在俄勒岡州的波特蘭。
Mirko Boehm從1997開始就是KDE的開發者,在1996年至2006年間是KDE e.V.委員會的
成員。他畢業于德國漢堡Helmut Schmidt大學的商業專業。在他的閑暇時間里,他閱讀
紙版書籍、與家人在一起,試圖遠離計算機。他目前在德國柏林為Klar?lvdalens
Datakonsult AB工作,負責跨平臺軟件和嵌入式軟件開發。
Christopher Dennis自2005年JCP項目開始時就是項目的主要開發者。Chris在牛津大學
讀博士時開始使用Java。此前,他使用過各種編程語言,從十六進制小鍵盤上編寫的
Z80機器碼到PHP和JavaScript。他對特殊情況、編碼技巧和偶爾有點丑陋的臨時編碼很
有興趣,喜歡用各種語言編寫緊湊的、優雅的代碼。
Dave Fetterman是Facebook的工程經理,他在那里創建了Facebook平臺項目。在2006
年加入Facebook之前,他是一名軟件工程師,參加Microsoft開發者部門的項目,包
括.NET的通用語言運行環境(CLR)。他喜歡為其他開發者創建軟件,也喜歡對愿意聽
的人發表長篇大論。他擁有應用數學的學士學位,并在2003年獲得了哈佛大學的計算機
科學碩士學位。
Keir Fraser是XenSource的創始人之一,XenSource現在是Citrix Systems公司的一部分。
他也是Xen系統管理程序的首席架構師。Keir在2002實現了Xen的第一個版本,作為他
InfoQ企業軟件開發叢書《架構之美》精選版
在劍橋計算機實驗室攻讀博士學位時的一項娛樂。在該項目成為大規模的社區合作的過
程中,他繼續作為主要的開發者。他因在無鎖并發控制方面的工作于2004年獲得了博士
學位,并在同年成為一名教師。
Pete Goodliffe是一名程序員、專欄作家、演說家和作家,從來不在同一軟件領域做過
多的停留。Pete的熱門書籍《Code Craft》(No Starch Press)是對整個編程追求的實際而
有趣的調查—大約600頁,真是了不起!他對制革很有熱情,而且不穿鞋。
Georgios Gousios是一名職業研究者,接受的教育和軟件工程有關,熱衷于軟件開發。
目前,他正在希臘的雅典經濟與商業大學完成他的博士論文。他的研究興趣包括軟件工
程、軟件質量、虛擬機和操作系統,他擁有英國曼徹斯特大學的科學碩士學位。
Gousios為多個開源軟件項目貢獻過代碼,并參與了各種學術項目和商業項目的研究與
開發。他是SQO-OSS項目的項目經理、設計權威和主要開發成員,為評估軟件質量探索
一些創新的方法。在他的學術生涯中,Gousios在會議和雜志上發表了10篇技術論文。
Gousios是ACM、IEEE、Usenix Association和Technical Chamber of Greece的成員。
Dave Grove是IBM的T.J. Watson研究中心動態優化組的一名研究員。他的主要研究興趣
包括分析和優化面向對象語言、虛擬機設計和實現、JIT編譯、在線反饋導向的優化和
垃圾收集。他在1998年參加了Jalape?o項目,是這個優化編譯器和適應式優化系統首個
實現的主要貢獻者。自Jalape?o在2001年作為Jikes RVM開放源碼以來,他一直是Jikes
RVM核心團隊和指導委員會的活躍成員。
John Klein是軟件工程研究所(SEI)的高級技術人員,他的研究方向是“眾系統之系
統”的架構方法,并幫助個人、團隊和組織機構改進他們的軟件架構能力。在加入SEI
之前,John是Avaya公司的首席架構師。在Avaya,他負責開發多模式的代理、通信分析
的架構,以及為各種客戶交互產品創建并改進架構。在此之前,John是Quintus的一名軟
件架構師,在那里他設計了第一款獲得商業成功的多渠道集成聯系中心產品,并導致了
Quintus兼并了另外兩家公司,實現了產品組合的技術集成。在加入Quintus之前,John
曾為多家視頻會議和視頻網絡業的公司服務。他的職業生涯開始于Raytheon,在那里他
為雷達信號處理、多光譜圖像處理、并行處理架構和算法提供硬件和軟件解決方案。
John擁有Stevens技術學院的學士學位和Northeastern大學的碩士學位。他是ACM和IEEE
計算機學會的成員。
Greg Lehey的漫長職業生涯在德國和澳大利業度過,他曾為德國空間研究所工作,也
曾為Univac、Tandem、Siemens-Nixdorf和IBM等計算機制造商工作,也曾作為一些沒名
氣的軟件公司的大客戶,還曾做過獨立的咨詢顧問。他的活動范圍很廣,包括從內核開
發到產品管理,從系統編程到系統管理,從處理衛星數據到為油泵編程,從生產
xiv 作譯者簡介
InfoQ企業軟件開發叢書《架構之美》精選版
CD-ROM到把自由軟件移植到DSP指令集上。他是FreeBSD核心團隊的成員,也是澳大
利業UNIX用戶協會的主席。他是FreeBSD和NetBSD項目的開發者,也是《Porting
UNIX Software》和《The Complete FreeBSD, Fourth Edition 》(O’Reilly)的作者。他還
以編寫商業應用軟件而聞名。Greg在2007年退休,將多出來的時間用于品味生活?,F在,
休閑活動占據了他的大多數時間,但這還不夠,他還聽古典木紋唱片、烹飪、釀啤酒
(他開發了一個計算機控制的發酵系統)、做園藝、騎馬和攝影。他也對一些歷史題材感
興趣,包括古代的難解的歐洲語言。他的主頁是:http://www.lemis.com/grop/。
Panagiotis Louridas在20世紀80年代通過一臺Sinclair ZX Spectrum開始涉足計算機。從
那時起,他就開始用機器語言進行編程,而且非常喜歡編程。他在雅典大學信息系獲得
了計算機科學學士學位,在曼徹斯特大學獲得了計算機碩士和博士學位。這些年來,他
一直為私人機構開發軟件,現在,他在希臘研究和教育網絡(GRNET)工作。他也是
雅典經濟與商業大學(AUEB)軟件工程和安全(SENSE)研究組的成員。他發表的文
章范圍很廣,從人類學到加密,從儀表展示到軟件工程。他特別喜歡尋找計算機世界和
其他領域的聯系。
Stephen J. Mellor在為軟件開發創建有效的工程方法方面,是國際公認的先行者。在
1985年,他出版了廣為閱讀的Ward-Mellor三卷本《Structured Development for Real-Time
Systems》(Prentice Hall);在1998年,他的書首次定義了面向對象分析。Stephen還在
2002年出版了《Executable UML: A Foundation for Model-Driven Architecture》(Addison-
Wesley Professional)。他最近的一本書《MDA Distilled: Principles of Model-Driven
Architecture》(Addison-Wesley Professional)在2004年出版。他在對象管理組織(OMG)
中活動積極,是為UML添加可執行動作的協會的主席,他最近完成了可執行UML的標準。
他是敏捷宣言的簽名者之一。他是OMG架構委員會的兩任成員,IEEE軟件顧問委員會的
主席,最近,他成為Mentor Graphics的嵌入式軟件部門的首席科學家。
Bertrand Meyer是ETH Zurich的軟件工程教授,也是Eiffel軟件的首席架構師,他領導
并設計了EiffelStudio環境和大量的庫。他是一些暢銷書的作者,其中包括獲得Jolt大獎
的《Object-Oriented Software Construction》(Prentice Hall)。他也因為在對象技術和
Eiff e l方面的工作獲得了ACM軟件系統大獎和Dahl-Nygaard大獎,并獲得了S t .
Petersburg州立技術大學的榮譽博士學位。他的研究對象涉及面向對象技術、編程語言、
軟件驗證(包括測試、并發和規范方法)。他也是一名活躍的顧問和講師。
William J. Mitchell是MIT架構和媒體藝術與科學系的Alexander Dreyfoos教授,他領導
著MIT媒體實驗室和MIT設計實驗室的Smart Cities團隊。他以前曾擔任MIT架構和計劃
學院的院長。他最近的新書是《World’s Greatest Architect》和《Imagining MIT》(都由
作譯者簡介xv
InfoQ企業軟件開發叢書《架構之美》精選版
MIT出版社出版)。
Derek Murray是劍橋大學計算機實驗室的博士生。他在2006年加入Xen項目,主要工作
是通過重新設計控制棧來改進Xen的安全性。他現在的研究主要是改進大規模分布式系
統的容錯性,但他還是偶爾會涉及系統核心。Derek在2006年從愛丁堡大學獲得了高性
能計算專業的碩士學位,2005年獲得了Glasgow大學的計算機學士學位。
Rhys Newman在十多年前于牛津大學完成博士學位時,就開始使用Java,那時Java還
只有幾年歷史。在他早期的研究中,他利用純Java環境展示了高性能實時場景處理的實
現方法,即使當時還是使用早期JIT化的JVM。從那時起,他同時在學界和業界工作,
一次次證明Java平臺實際上有多靈活、多高效、多快。在超過20年的軟件工程生涯中,
他獲得了多個業界杰出技術獎項,最近他回到了牛津,承擔了網格計算領域的突破性研
究工作。JPC是最新研究工作的一部分。
Michael Nygard致力于在全國幫助開發者提高水平和減少痛苦。他和他遇到的每一個人
分享他對改進的熱情和活力,有時甚至沒有得到對方的同意。Michael花了20年中的大
部分時間學習對專業程序員有意義的事,他關心藝術、質量和技藝。他總是愿意在那些
全職的、真心投入工作的開發者(那些“覺醒的”開發者)身上花時間。在另一方面,
他不能容忍缺乏興趣或浪費潛力。Michael在近20年來一直是專業的程序員和架構師。
在這段時間里,他為美國政府、軍方、銀行、金融業、農業和零售業交付了運營系統。
通常,Michael都要面對他自己開發的系統。這種實際運營的經歷改變了他對軟件架構
和開發的看法。他參與了一個Tier 1零售網站的初期開發,并且常常作為其他在線業務
的“流動解決問題專家”。這些經驗讓他對在相當不友好的環境下構建高性能、高可靠
性的軟件有了獨特的看法。最近,Michael編寫了《Release It! Design and Deploy
Production-Ready Software》(Pragmatic Programmers),該書獲得了2008年的Jolt生產力
大獎。他的其他文字可以在http://www.michaelnygard.com/blog上讀到。
Ian Rogers是曼徹斯特大學高級處理器技術研究組的研究員。他的博士研究工作是關于
Dynamite二進制翻譯器的,該技術實現了商用,現在是許多二進制翻譯器產品的一部分,
包括Apple的Rosetta。他最近的學術研究工作一直是編程語言設計、運行時環境和虛擬
機環境,特別是如何自動創建它們并有效地使用并行技術。他是Jikes研究虛擬機的主要
貢獻者,是開發團隊的核心成員。
Brian Sletten是自由的、受過藝術教育的軟件工程師,關注forward-learning技術。他曾
擔任過系統架構師、開發者、現場指導者和培訓師。他在世界各地的會議上發表演講,
xvi 作譯者簡介
編輯注: 此書由機械工業出版社引進出版,書名為《代碼質量》(注釋版),書號為:978-7-
111-22671-0。
InfoQ企業軟件開發叢書《架構之美》精選版
并為一些在線出版物編寫關于面向Web技術的文章。他的經驗涉及國防、金融和商業領
域。他曾設計并建造了網絡矩陣式交換控制系統、在線游戲、3D仿真/可視化環境、因
特網分布式計算平臺、P2P和基于Web的語義系統。他擁有William and Mary大學的計算
機科學學士學位,目前居住在弗吉尼亞的Fairfax。他是Bosatsu咨詢公司的總裁,該公
司為Web架構、面向資源的計算、語義Web、高級用戶界面、可伸縮系統、安全和其他
20世紀末21世紀初的技術提供專業的咨詢服務。
Diomidis Spinellis是希臘雅典經濟與商業大學管理科學與技術系的副教授。他的研究領
域包括軟件工程、計算機安全和編程語言。他也編寫了兩本“開放源碼方面”的書,由
Addison-Wesley出版:《Code Reading》(獲得了2004年的軟件開發生產力大獎)和
《Code Quality》(獲得了2007年軟件開發生產力大獎,編輯注)。他也寫了幾十篇科學論
文。他是IEEE Software編輯委員會的成員,負責定期的“Tools of the Trade”欄目。
Diomidis是FreeBSD的提交者,也是UMLGraph和其他開源軟件包、庫和工具的開發者。
他擁有軟件工程的碩士學位和計算機科學博士學位,都是在Imperial College London獲
得的。Diomidis是ACM的高級成員,也是IEEE和Usenix Association的成員。
Jim Waldo是Sun微系統實驗室的杰出工程師,負責研究下一代大規模分布式系統。他
目前是Project Darkstar的技術負責人,該系統是針對大規模多人在線游戲和虛擬世界而
設計的多線程、分布式基礎設施。在此之前,他曾是Jini的首席架構師,Jini是基于Java
的分布式編程系統。Jim編寫了《The Evolution of C++: Language Design in the
Marketplace of Ideas》(MIT出版社),也是《The Jini Specification《Addison-Wesley》
的合著者之一。他曾是美國國家學術委員會的共同主席,編輯并出版了《Engaging
Privacy and Information Technology in a Digital Age》一書。Jim也是哈佛大學的輔助教
師,在計算機科學系教授分布式計算和策略與技術相關的內容。Jim擁有馬薩諸塞大學
(Amherst)的哲學博士學位。
David Weiss擁有Union College的計算機科學學士學位,并擁有馬里蘭大學的計算機科
學碩士和博士學位。他目前是Avaya實驗室的軟件技術研究部的領導,他關注軟件開發
效率改進的普遍問題和Avaya軟件開發過程改進的特殊問題。在第二個問題上,他領導
了Avaya軟件技術研究中心。以前,他曾是朗迅技術貝爾實驗室軟件生產研究部的主任,
該部門負責研究如何改進軟件開發的效率。在加入貝爾實驗室之前,他是軟件生產力協
會(SPC)復用和度量部門的主任,該協會由14個大型的美國航空公司組成。在加入
SPC之前,Weiss博士在技術評估辦公室度過了一年的時間,在那里他與同事共同完成了
Strategic Defense Initiative的技術評估。在1985—1986學年,他是Wang Institute的訪問
學者,在許多年里,他一直是華盛頓特區Naval研究實驗室(NRL)計算機科學和系統
部門的研究員。他也是一名程序員和數學家。Dave的主要研究方向是軟件工程領域,特
作譯者簡介xvii
InfoQ企業軟件開發叢書《架構之美》精選版
別是軟件開發過程和方法學、軟件設計和軟件測量。他最為人知的是發明了軟件測量的
“目標-問題-測量指標”方法,軟件系統模塊化結構的工作,以及軟件生產線工程的工作。
他是Synthesis過程及其后繼FAST過程的共同發明人。他與別人共同編著了兩本書:
《Software Product-Line Engineering》和《Software Fundamentals: Collected Papers of
David L. Parnas》(都由Addison-Wesley出版)。
譯者簡介
王海鵬1994年畢業于華東師范大學。擁有理學士(物理)和文學士(英國語言文學)
學位。獨立的咨詢顧問、培訓講師、譯者和軟件開發者。已翻譯十余本軟件開發書籍,
主題涵蓋敏捷方法學、需求工程、UML建模和測試。擁有15年軟件開發經驗,目前主
要的研究領域是軟件架構和方法學,致力于提高軟件開發的品質和效率。
蔡黃輝江蘇啟東人。1999年畢業于上海交通大學,畢業后一直從事軟件開發工作,主
要使用Java做Web方面的底層開發。現居住在上海。聯系方式: caihuanghui @
hotmail.com。
徐鋒中國系統分析員顧問團(CSAI)軟件工程首席顧問,中國軟件技術大會杰出貢獻
專家,資深咨詢顧問。主要研究領域為需求工程、系統分析與設計、軟件估算,致力于
推動軟件工程方法論的落地應用。曾在《程序員》等媒體發表了《實戰OO》、《項目管
理三步曲》、《大話Design》等多個專欄文章,著有《軟件需求最佳實踐》、《UML面向對
象建模基礎》等多本書籍,翻譯了《UML 2.0實戰》、《AOSD中文版》、《Cloud to Code
中文版》等多本相關技術書籍。
xviii 作譯者簡介
InfoQ企業軟件開發叢書《架構之美》精選版
第1 章
架構概述
John Klein
David Weiss
1.1 簡介
建筑師、音樂家、作家、計算機設計師、網絡設計師和軟件開發者都在使用“架構”這個
術語,其他人也用(你有沒有聽說過“食物架構”?),然而不同的用法其結果也不同。
建筑與交響樂完全不同,但都有架構。而且,所有的架構師都在談論他們工作中的美,以
及因此而導致的結果。建筑師可能會說,一座建筑應該提供適合工作或生活的環境,而且
它應該看起來很美。音樂家可能會說,音樂應該能演奏,包含能夠辨明的主題,而且它應
該聽起來很美。軟件架構師可能會說,系統應該對用戶友好、響應及時、可維護、沒有重
大錯誤、易于安裝、可靠,應該通過標準的方式與其他系統通信,而且也應該是美的。
這本書為你提供了一些美麗架構的詳細例子,它們來自于各類計算機系統。相對來說,
計算機是比較年輕的一個學科。因為年輕,所以不像建筑、音樂或寫作等領域那樣,有
那么多的例子;也因為年輕,則需要更多的例子。我們希望這本書能滿足這種需要。
在你開始研究這些例子之前,我們希望你考慮以下兩個問題:1) 什么是架構?2) 美麗
的架構都有哪些特性?你會在這一章中看到架構的不同定義,每個學科都有自己的定義,
所以我們將首先探討不同學科中的架構有何共同點,以及人們試圖用架構解決哪些問題。
具體來說,架構有助于確保系統能夠滿足其利益相關人的關注點,在構想、計劃、構建
和維護系統時,架構有助于處理復雜性。
13
InfoQ企業軟件開發叢書《架構之美》精選版
1
然后我們將介紹架構的定義,展示如何將這個定義應用于軟件架構,因為軟件是本書后
面大部分例子關注的核心。這個定義的關鍵在于,架構由一組結構組成,這些結構的設
計目的是讓架構師、構建者,以及其他利益相關人看到他們的關注點是如何得到滿足的。
在本章末尾,我們將討論美麗架構的特性,并引用一些例子。美的核心在于概念完整性—
即一組抽象和規則,在整個系統中盡可能簡單地應用它們。
在討論中,我們將“架構”作為一個名詞,它意味著一組工件,包括像藍圖和構建規范
這樣的文檔。這些工件描述了要構建的對象,在這種描述中,該對象被視為一組結構。
某些人也把“架構”作為一個動詞,用來描述創建這些工件的過程,包括由此而導致的
工作。然而,正如Jim Waldo和其他人曾指出的,沒有什么過程可以保證你學了以后就
能創造出好的系統架構,更不必說美的架構了(Waldo 2006),所以我們將更關注工件,
而非過程。
架構:“建造的藝術或科學;特別是設計和建造人類使用的建筑時的藝術或實
踐,同時考慮到美學因素和實用因素。”
—《The Shorter Oxford English Dictionary》(小型牛津英語字典,第5版)
在所有學科中,架構都提供了一種方式來解決共同的問題:確保建筑、橋梁、樂曲、書
籍、計算機、網絡或系統在完成后具有某些屬性或行為。換言之,架構既是所構建系統
的計劃,確保由此得到期望的特性,同時也是所構建系統的描述。維基百科上說:“根
據這方面已知最早的著作,即Vitruvius的‘On Architecture’,好的建筑應該美觀
(Venustas)、堅固(Firmitas)、實用(Utilitas);架構可以說是這三方面的一種平衡和
配合,沒有哪一個方面比其他方面更重要?!?br /> 我們談到交響樂的“架構(architecture)”,反過來,又將架構(architecture)
稱為“凝固的音樂”。
—Deryck Cooke, 《The Language of Music》(音樂的語言)
好的系統架構展示了架構完整性。也就是說,它來自于一組設計規則,這組規則有助于
減少復雜性,并可以用于指導詳細設計和系統驗證。設計規則可能包含特定的抽象,這
些抽象總是以同樣的方式使用,諸如虛擬設備等。這些規則可能表現為一種模式,如管
道和過濾器。在最理想的情況下,存在一些可以用于驗證的規則,如“在設備失效時,
所有某一類的虛擬設備都可以用任何其他同類的虛擬設備代替”,或“所有競爭同一資
源的進程必須具有相同的調度優先級”。
當代的架構師可能會說,待構建的對象或系統必須具有以下特征:
? 具備客戶要求的功能。
? 能夠在要求的工期內安全地構建。
14 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
2
? 性能足夠好。
? 可靠的。
? 可用的,并且使用時不會造成傷害。
? 安全的。
? 成本是可以接受的。
? 符合法規標準。
? 將超越前人及其競爭者。
我們將計算機系統的架構定義為一組最小的特征集,它們決定了哪些程序將運
行,以及這些程序將得到什么結果。
—Gerrit Blaauw 和Frederick Brooks, 《Computer Architecture》(計算機體系結構)
我們從來沒有看到過一個復雜系統能夠很好地滿足上述特征。架構是一種折中—決定
改進其中一個特征常常會對其他特征產生負面影響。架構師必須確定怎樣做是足夠好的,
方法就是發現特定系統的重要關注點,以及充分滿足這些關注點的條件。
架構觀點中的常見思想是結構,每種結構都由各種類型的組件及其關系構成:它們如何
組合、相互調用、通信、同步,以及進行其他交互。組件可以是建筑中的支架橫梁或內
部腔室、交響樂中的旋律、故事中的章節或人物、計算機中的CPU和內存、通信棧中的
層或連接到一個網絡上的處理器、協作的順序過程、對象、編譯時的宏、構建時的腳本。
每個學科都有自己的一套組件和組件間的相互關系。
從更大的范圍來說,術語“架構”總是意味著“不變的深層次結構”。
—Stewart Brand, 《How Buildings Learn》
面對不斷增長的系統復雜性,以及它們內部和相互之間的交互,由一組結構形成的架構
提供了對付復雜性的主要手段,目的是確保得到的系統具備所要求的特征。結構為我們
提供途徑,將系統化解為一些交互的組件。
每種結構都是為了幫助架構師理解如何來滿足特定的關注點,如可變性或性能。展示某
些關注點得到滿足時,可能會影響到其他方面的關注點,但架構師必須能夠說明所有關
注點都已得到滿足。
網絡架構:構成一個網絡的通信設備、協議和傳輸鏈路,以及它們的組織方式。
—http://www.wtcs.org/snmp4tpc/jton.htm
架構概述15
InfoQ企業軟件開發叢書《架構之美》精選版
3
1.1.1 建筑師的角色
在設計、構建和修復建筑時,我們指定關鍵的設計師為“建筑師(architects)”,并賦予
他們廣泛的職責。建筑師準備建筑最初的草圖,展示外觀和內部布局,與客戶討論這些
草圖,直至所有相關方都達成一致意見,認為展示的就是他們想要的。這些草圖是抽象:
它們關注建筑中某些方面的適當細節,而忽略其他的內容。
當客戶和建筑師在這些抽象上達成一致意見之后,建筑師會準備或監督準備更為詳細的
圖紙,以及相關的文字規格說明。這些圖紙和規格說明描述了建筑的許多“實質性”細
節,如管道、壁板材料、窗戶玻璃和電線等。
在極少的情況下,建筑師簡單地將詳細規劃交給建造者,建造者將根據規劃完成項目。
對更重要一些的項目,建筑師會繼續參與,定期檢查工作,并且可能會建議變更,或接
受來自建造者和客戶的變更建議。如果建筑師監督項目,僅當他確認項目充分符合了規
劃和規格說明的要求,項目才算完工。
我們請一名建筑師是為了確保:1)設計滿足客戶的需要,包括前面提到的那些特征;2)
設計具有概念完整性,處處運用了相同的設計原則;3)設計滿足法規和安全的要求。
建筑師職責的一個重要方面是確保設計概念在實現時得到一致的體現。有時候,建筑師
也充當建造者和客戶之間的協調人。哪些決定需要由建筑師做出,哪些決定由其他人做
出,人們對這個問題常有不同意見,但我們清楚,建筑師將做出重要決定,包括所有對
結構的可用性、安全性和可維護性產生影響的那些決定。
音樂作曲與軟件架構
雖然人們常用建筑架構設計來類比軟件架構,但音樂作曲可能是更好的
類比。建筑師創建的是相對靜止的結構(該架構必須考慮到人員和服務
在建筑內的移動,以及承重結構)的靜態描述(藍圖或其他圖紙)。在
音樂作曲和軟件設計中,作曲家(軟件架構師)創建一段音樂的靜態描
述(架構描述和代碼),這段音樂以后將演奏(執行)許多次。在音樂
和軟件中,設計都依靠許多組件的交互來得到期望的結果,結果依賴于
演奏者、演奏環境,以及演奏者所做的詮釋。
1.1.2 軟件架構師的角色
軟件開發項目需要一些人在軟件構建時扮演架構師的角色,就像構建或修復建筑時傳統
的建筑師的角色一樣。但是,對于軟件系統來說,從來就弄不清楚哪些決定屬于架構師
的職責范圍,哪些決定要留給實現者。定義架構師在軟件項目中做什么,比建筑師的類
16 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
4
似定義更困難, 原因有3 個因素: 缺少傳統、產品無形性和系統復雜性。( 參見
Grinter[1999],其中描述了軟件架構師如何在一個大型軟件開發組織中實現她的職責。)
具體來說:
? 建筑師可以回顧幾千年的歷史,看看過去的建筑師都做過些什么。他們可以參觀
并研究那些矗立了幾百年的建筑,有時甚至有上千年歷史的建筑,而它們仍在使
用。在軟件業,我們只有幾十年的歷史,并且我們的設計常常是不公開的。此外,
建筑師擁有并利用標準來描述他們制作的圖紙和規格說明,這讓現在的建筑師能
夠從記錄下來的架構歷史中受益。
? 建筑是有形的產品,在建筑師制作的規劃和工人修造的建筑之間存在著明顯的區別。
架構復用
圣索菲亞大教堂(Hagia Sophia,上圖),建造于公元6世紀,率先使用了所
謂的“穹頂”結構來支撐巨大的圓形屋頂,它是拜占庭建筑之美的代表。
在1100年之后,Christopher Wren使用了同樣的設計來建造圣保羅大教堂的
穹頂(St. Paul’s Cathedral,下圖),它成為倫敦的地標性建筑。這兩座建筑
在今天仍在使用。
架構概述17
InfoQ企業軟件開發叢書《架構之美》精選版
5
在大的軟件項目中,常常會有許多架構師。某些架構師相當專注于特定領域,如數據庫
和網絡,他們一般作為團隊的一部分,但目前我們假定只有唯一一位架構師。
1.1.3 軟件架構的含義
如果認為“架構”是一個簡單的實體,能夠用一份文檔或一張圖紙來描述,那就錯了。
架構師必須做出許多設計決定。要想有用,這些決定必須用文檔記錄下來,這樣就能夠
進行復審、討論、修改和批準,然后作為后續決定和構建時的約束。對于軟件系統,這
些設計決定包括行為上的和結構上的。
外部行為描述展示了產品如何與它的用戶、其他系統和外部設備進行交互,這應該表現
為需求。結構描述展示了產品如何劃分為多個部分,以及這些部分之間的關系。我們還
需要內部行為描述,用于描述組件之間的交互接口。結構上的描述常常展示相同部分的
一些不同視圖,因為不可能把所有信息以有意義的方式組織到一張圖紙或一份文檔中。
一個視圖中的組件,可能是另一個視圖中一個組件的一個部分。
軟件架構常常表現為分層的層次結構,這種層次結構將幾種不同的結構放在一張圖中。
20世紀70年代,Parnas指出“層次結構”這個術語已經被濫用,然后精確地定義了它,
并給出了幾個不同結構的例子,它們在設計不同系統時實現了不同的目的(Parnas 1974)。
將架構的結構描述為一組視圖(view),每個視圖關注不同的部分,現在已成為了廣泛
接受的標準架構實踐(Clements等2003; IEEE 2000)。我們將使用“架構”這個詞來代
指一組有標注的圖紙和功能描述,它說明了設計和構建一個系統時所使用的結構。在軟
件開發社區中,針對這樣的圖紙和描述,人們使用并建議了許多不同的形式。在
Hoffman和Weiss(2000,第14章和第16章)的著作中可以看到一些例子。
一個程序或計算系統的軟件架構是系統的一種結構或一組結構,它包含軟件元
素、這些元素的外部可見的屬性,以及元素之間的關系。
“外部可見”的屬性是其他元素對該元素可以做出的假定,諸如它提供的服務、
執行時的特征、錯誤處理、共享資源的使用等。
—Len Bass、Paul Clements和Rick Kazman
《Software Architecture in Practice, Second Edition》
1.1.4 架構與設計
架構是系統設計的一部分,它突出了某些細節,并通過抽象省略掉另一些細節。所以,
架構是設計的一個子集。關注實現系統組件的開發者可能不會特別關心所有組件如何裝
配在一起,而是主要關注少數組件的設計和開發,包括他們必須遵守的架構約束和可以
應用的規則。因此,開發者和架構師面對的是系統設計的不同方面。
18 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
6
如果說架構關注的是組件之間的關系和系統組件外部可見的屬性,那么設計還要關注這
些組件的內部結構。例如,如果一組組件包含了一些信息隱藏的模塊,那么這些外部可
見的屬性就構成了這些組件的接口,內部的結構與模塊內的數據結構和控制流一同考慮
(Hoffman和Weiss 2000,第7章和第16章)。
1.2 創建軟件架構
到目前為止,我們已經討論了一般意義上的架構,并分析了軟件架構與其他領域的架構
之間有何相似與差異。接下來我們將注意力轉到“如何”設計軟件架構。當架構師創建
軟件系統的架構時,她應該關注什么?
軟件架構師的首要關注點不是系統的功能。
這是正確的—軟件架構師的首要關注點不是系統的功能。
例如,如果我們請你來設計一個“基于Web的應用”,你首先問我們頁面布局和導航樹,
還是問下面這些問題:
? 誰提供應用主機托管?托管的環境有什么技術限制嗎?
? 你想運行在Windows服務器上還是在LAMP棧上?
? 你想支持多少并發用戶?
? 應用需要怎樣的安全性?有需要保護的數據嗎?應用將運行在公網上還是在私有
的內部網上?
? 你能為這些答案排列優先級嗎?例如,用戶數是否比響應時間更重要?
根據我們對這些問題和一些其他問題的回答,你就可以開始畫出系統架構的草圖。我們
還沒有談到應用的功能。
好吧,我們承認耍了點計謀,因為我們問的是“基于Web的應用”,這是一個大家熟悉
的領域,所以你已經知道了哪些決定會對你的架構產生最大的影響。類似地,如果我們
問的是一個電信系統或一個航空電子控制系統,在這些領域有經驗的架構師將考慮到一
些功能需求。但是,你仍然可以不必過多擔心功能就開始設計架構。你關注的是需要滿
足的品質。
品質關注點指明了功能必須以何種方式交付,才能被系統的利益相關人所接受,系統的
結果包含這些人的既定利益。利益相關人有一些關注點,架構師必須重視。稍后,我們
將討論為了確保系統具有要求的品質,通常會提出的一些關注點。正如我們前面所說的,
架構師的一項職責是確保系統設計能滿足客戶的需要,我們將利用品質關注點來幫助我
們理解這些需要。
架構概述19
InfoQ企業軟件開發叢書《架構之美》精選版
7
這個例子突出了成功架構師的兩項關鍵實踐:讓利益相關人參與以及同時關注功能和品
質。作為一名架構師,你首先問我們想從系統中得到什么,有怎樣的優先級。在實際項
目中,你會找出其他的利益相關人。典型的利益相關人和他們的關注點包括:
? 投資人,他們想知道項目是否能夠在給定的資源和進度約束下完成。
? 架構師、開發人員和測試人員,他們首先考慮的是最初的構建和以后的維護與演進。
? 項目經理,他們需要組織團隊,制定迭代計劃。
? 市場人員,他們想通過品質特點實現與競爭者的差異化。
? 用戶,包括最終用戶、系統管理員,以及安裝、部署、準備、配置人員。
? 技術支持人員,他們關注幫助平臺電話呼入的數目和復雜性。
每個系統都有自己的品質關注點。有些關注點可能定義得很好,如性能、安全、可伸縮
性等。但是,另一些同樣重要的關注點卻可能沒有詳細規定,如可變性、可維護性和可
用性等。利益相關人希望把功能放到軟件上,而不是放到硬件上,這主要是為了很容易、
很快速地修改,然后通常在品質關注方面又對可變性輕描淡寫。這很奇怪,不是嗎?哪
些改變能夠迅速、容易地實現,哪些改變需要花時間并且很難實現,架構決定將對此產
生重要影響。所以,架構師難道不應該在理解功能需求的同時,也理解利益相關人在
“可變性”這樣的品質方面的期望嗎?
當架構師理解了利益相關人的品質關注點之后,接下來該做些什么?考慮折中。例如,
對信息加密將加強安全性,但會損失性能。利用配置文件將增加可變性,但會降低可用
性,除非我們能夠驗證配置是有效的。我們是否應該對這些文件使用標準的表示方式,
如XML,還是使用自己發明的格式?創建系統的架構將涉及許多這樣的艱難折中。
架構師的第一項任務,就是與利益相關人協作,理解這些品質關注點和約束,并為它們
排列優先級。為什么不從功能需求開始?因為通常有許多種可能的系統分解方式。例如,
從數據模型開始可能得到一種架構,而從業務處理模型開始則可能得到不同的架構。在
極端的情況下,系統沒有分解,被開發成單一的軟件。這可能會滿足所有的功能需求,
但可能不會滿足品質需求,如可變性、可維護性、可伸縮性等。架構師通常必須進行架
構層面的系統重構,例如為了滿足伸縮性或性能的要求,將單機部署遷移到分布式部署,
從單線程轉向多線程,或者將硬編碼的參數移到外部配置文件中,因為原來從不改變的
參數現在需要修改了。
盡管有許多架構都能滿足功能需求,其中卻只有一少部分能夠滿足品質需求。讓我們回
到Web應用的例子。請考慮提供Web頁面的諸多方式—Apache和靜態頁面、CGI、
Servlet、JSP、JSF、PHP、Ruby on Rails、ASP.NET等。選擇其中的一種技術是一種架
構決定,它將對你滿足特定品質需求的能力產生重要影響。例如,像Ruby on Rails這樣
20 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
8
的方式可能提供快速推向市場的好處,但可能更難維護,因為Ruby語言和Rails框架都
在不斷地快速發展。也許我們的應用是基于Web的電話,我們需要讓電話“響鈴”。如
果你為了滿足性能的要求,需要從服務器向Web頁面發出真正異步的事件,那么基于
Servlet的架構可能更容易測試和修改。
在真實的項目中,滿足利益相關人的關注點需要做出更多的決定,而不僅是選擇一個
Web框架。你是否真的需要一個“架構”,并需要一名“架構師”來做出這些決定?誰
將做出這些決定?是編程人員嗎?他們可能會做出許多無意識的、隱含的決定。還是由
架構師來做出這些決定?他們全面了解整個系統、利益相關人和系統的演進,然后做出
明確的決定。不論哪種方式,你會有一個架構。它是否應該明確地形成并記入文檔?或
者它應該是隱式的,需要通過閱讀代碼才能發現?
當然,這種選擇通常不是這么死板。但是,隨著系統的規模、復雜度和開發人員數目的
增長,這些早期決定以及它們的記錄方式將產生越來越大的影響。
我們希望你現在已經理解,如果你的系統要滿足其品質要求,架構決定是很重要的,你
需要注意架構,有意識地做出這些決定,而不只是“讓架構自動出現”。
如果系統非常大,情況會怎樣?我們之所以運用“分而治之”這樣的架構原則,一個原
因就是為了降低復雜性,讓工作能夠并發進行。這讓我們能夠創建越來越大的系統。架
構本身是否能夠分解為多個部分,這些部分是否能由不同的人并行開發?考慮到計算機
的架構,Gerrit Blaauw和Fred Brooks斷定:
??如果,在采取了所有讓任務能夠由單人處理的方法之后,架構任務仍然巨
大而復雜,不能由一人來完成,那么產品肯定是太復雜了,以致不實用且不應
構建。換言之,單個用戶必須能夠理解計算機的架構。如果計劃的架構不能由
一個人設計,那它也不能被一個人理解。(1997)
你是否需要理解架構的所有方面,才能使用它?架構會分離關注點,所以在大多數情況
下,利用架構來構建或維護系統的開發人員或測試人員,不需要一下面對全部的架構,
而是只要面對必要的部分,就能完成指定的功能。這讓我們能夠創建超出個人可以理解
的、更大的系統。但是,在我們完全忽略IBM System/360(最長壽的計算機架構之一)
創造者的建議之前,讓我們先來看看他們為什么這樣說。
Fred Brooks說,概念完整性是架構最重要的特征:“最好是讓系統??反映一組設計思
想,而不是讓系統包含許多好的思想,而這些思想卻彼此獨立而不協調”(1995)。正是
這種概念完整性,讓開發者在知道了系統的一部分之后,能夠迅速理解系統的另一部分。
概念完整性來自于處理問題的一致性,如分解的判據、設計模式的應用和數據模式。這
讓開發者運用在系統中的一部分工作的經驗,來開發和維護系統的其他部分。同樣的規
架構概述21
InfoQ企業軟件開發叢書《架構之美》精選版
9
則應用于整個系統各處。當我們轉向“眾系統之系統”時,在集成了這些系統的架構中
也必須保持概念完整性。例如,可以選擇發布/訂閱消息總線這樣的架構風格,然后將
這種風格統一地應用于“眾系統之系統”的系統集成中。
架構團隊的挑戰在于,在創建架構時保持同一種思考方式和同一種哲學。讓團隊保持盡
可能小,讓他們在充分溝通、高度協作的環境工作,讓一兩個“首席架構師”擔任仁慈
的獨裁者,最終做出所有決定。這種架構模式常見于成功的團隊,不論是公司開發還是
開源開發,由此而得到的概念完整性是美麗架構的一種特性。
好的架構師通常來自于更好的架構師提供的現場指導(Waldo 2006)。原因之一可能是
有一些關注點幾乎在所有項目中都會出現。我們已經提到過一些,但這里有一份更完整
的清單。每個關注點都以問題的方式表述,架構師在項目過程中可能需要考慮它。當然,
具體系統會有其他關鍵的關注點。
功能性(Functionality)
產品向它的用戶提供哪些功能?
可變性(Changeability)
軟件將來可能需要哪些改變?哪些改變不太可能發生,不需要特別容易進行這些改變?
性能(Performance)
產品將達到怎樣的性能?
容量(Capacity)
多少用戶將并發使用該系統?該系統將為用戶保存多少數據?
生態系統(Ecosystem)
在部署的生態環境中,該系統將與其他系統進行哪些交互?
模塊化(Modularity)
如何將編寫軟件的任務分解為工作指派(模塊),特別是這些模塊可以獨立地開發,
并能夠準確而容易地滿足彼此的需要?
可構建性(Buildability)
如何將軟件構建為一組組件,并能夠獨立實現和驗證這些組件?哪些組件應該復用
其他的產品,哪些應該從外部供應商處獲得?
產品化(Producibility)
22 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
10
如果產品將以幾種變體的形式存在,如何開發一個產品線,并利用這些變體的共
性?產品線中的產品以怎樣的步驟開發(Weiss和Lai 1999)?在創建一條軟件產品
線時,要進行哪些投資?開發產品線中不同變體的選擇,預期會得到怎樣的回報?
特別是,是否可能先開發最小的有用產品,然后再添加(擴展)組件,在不改變以
前編寫的代碼的情況下,開發產品線的其他成員?
安全性(Security)
產品是否需要用戶認證,或者必須限制對數據的訪問?數據的安全性如何得到保
證?如何抵擋“拒絕服務”攻擊或其他攻擊?
最后,一個好的架構師會認識到,架構會影響組織機構。Conway指出,系統的結構會
反映構建它的組織機構的結構(1968)。架構師可能會認識到,Conway法則可以反過來
應用。換言之,一個好的架構可能對組織機構產生影響,讓組織機構發生改變,從而更
有效地從該架構構建出系統。
1.3 架構結構
那么,好的架構師如何來處理這些關注點?我們曾經提到過,需要將系統組織成一些結構,
每種結構都定義了特定類型的組件之間的具體關系。架構師的主要關注點就是對系統進行
組織,讓每種結構有助于解答一個關注點所定義的問題。關鍵的結構決定將產品劃分為組
件,并定義了這些組件之間的關系(Bass、Clements和Kazman 2003; Booch、Rumbaugh和
Jacobson 1999; IEEE 2000; Garlan和Perry 1995)。對于任何產品,都有許多結構需要設計。
每種結構都必須單獨設計,這樣它就表現為一個獨立的關注點。在接下來的幾節中我們會
討論一些結構,你可以利用它們來考慮前面列表中的關注點。例如,“信息隱藏結構”展示
了如何將系統組織成一些工作指派。這種結構也可以用作改變的路線圖,展示了建議的改
變,以及哪些模塊支持這些改變。針對每種結構,我們描述了一些組件及其之間的關系,
正是這些組件和關系確定了這種結構。對照前面的列表,我們認為下面的結構是最重要的。
1.3.1 信息隱藏結構
組件與關系:主要組件是一些“信息隱藏模塊”,每個模塊都是針對一組開發人員的工
作指派,每個模塊都包含了一種設計決定。如果一項決定可以改變,同時又不影響任何
其他模塊,我們就說這項設計決定是一個模塊的秘密(Hoffman和Weiss 2000,第7章和
第16章)。模塊間最基本的關系是“整體-部分”關系。如果“信息隱藏模塊A”的秘密
是“信息隱藏模塊B”的秘密的一部分,那么A就是B的一部分。請注意,必須能夠在改
變A的秘密的同時,不改變B的其他部分。否則,根據我們的定義,A就不是B的一個子
模塊。例如,許多架構都有一些虛擬設備模塊,它們的秘密是如何與特定的物理設備通
架構概述23
InfoQ企業軟件開發叢書《架構之美》精選版
11
信。如果虛擬設備分成不同類型,那么每種類型可能構成該虛擬設備模塊的一個子模塊,
其中每種虛擬設備類型的秘密將是如何與這種類型的設備進行通信。
每個模塊都是一份工作指派,包含了一組要寫的程序。根據不同的語言、平臺、環境,
“程序”可以是能在計算機上執行的方法、過程、函數、子程序、腳本、宏或其他指令
序列。第二種信息隱藏模塊結構是基于程序和模塊之間的“包含”關系。如果模塊M的
一部分工作指派是要編寫程序P,那么M就包含P。請注意,每個程序都包含在一個模塊
中,因為每個程序必然是某些開發人員的工作指派的一部分。
這些程序中的一些可以通過模塊的接口來訪問,而另一些則是內部的。模塊也可能通過
接口發生關系。A模塊的接口是一組假定,這些假定包括該模塊之外的程序可以對該模
塊做出的假定,也包括該模塊中的程序對其他模塊的程序和數據結構所做的假定。如果
改變B的接口就要求A也發生改變,那么我們就說A“依賴”B的接口。
“整體-部分”結構是層次狀的。在這個層次結構的葉節點上的模塊不包含可識別的子模
塊?!鞍苯Y構也是層次狀的,因為每個程序都只包含在一個模塊之中。“依賴”關系
不一定是層次狀的,因為兩個模塊可能互相依賴,要么是直接互相依賴,要么是通過一
個較長的“依賴”關系形成的環。請注意,“依賴”不應該與后面小節中定義的“使用”
混淆起來。
信息隱藏結構是面向對象設計方法的基礎。如果一個信息隱藏模塊設計為一個類,這個
類的公有方法就屬于該模塊的接口。
滿足的關注點:信息隱藏結構的設計應該能滿足可變性、模塊化和可構建性的要求。
1.3.2 使用結構
組件與關系:根據前面我們的定義,信息隱藏模塊包含一個或多個程序(在上一小節中
定義)。當且僅當兩個程序共享一個秘密時,它們才屬于同一個模塊?!笆褂媒Y構”(Uses
Structure)的組件是一些可以單獨調用的程序。請注意,程序可以相互調用,或被硬件
調用(例如,被一個中斷例程調用),調用也可能來自于不同命名空間的程序,如操作系
統例程或遠程過程。而且,調用發生的時間可以是任何時候,從編譯時到運行時。
只有在相同綁定時間操作的程序之間,我們才考慮形成一種使用結構。首先只考慮運行
時操作的程序可能最容易。以后,我們也可以考慮那些編譯時或載入時操作的程序之間
的使用關系。
如果程序B必須存在并滿足其規格說明,程序A才能滿足其規格說明,我們就說A使用了
B。換言之,B必須存在且操作正常,A才能操作正常。使用關系有時候也稱為“要求存
在正確的版本”。進一步的解釋和例子,參見(Hoffman和Weiss 2000)的第14章。
24 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
12
使用結構確定了我們可以構建并測試怎樣的工作子集。在軟件系統的使用結構中,期望
的屬性是它定義了一種層次結構,這意味著其中不出現環。如果在使用關系中出現環,
那么環中所有程序都必須存在且正常工作,才能讓其他的程序正常工作。由于也許不能
夠創建完全沒有環的使用關系,架構師可能將使用環中的所有程序作為單一的程序,以
這種方法來創建子集。子集必須要么包含全部程序,要么都不包含。
如果在使用關系中沒有環,軟件采用的就是一種層次結構。在最底層,即第0層,是所
有不使用其他程序的程序。第n層包含了所有的程序,它們使用了第n-1層或以下層的
程序。這些層常常描繪為一系列的層次,每個層次表示了使用關系中的一個或幾個層。
在使用結構中對相鄰的層分組,有助于簡化表示,并允許在關系中出現小環的情況。進
行這種分組有一個指導原則,即一個層次中的程序應該比它上一個層次中的程序執行速
度快9倍,執行頻率高9倍(Courtois 1977)。
具有層次使用結構的系統可以同時構造一層或幾層。這些層次有時候稱為“抽象層”,
但這是一種錯誤的名稱。因為這些組件是獨立的程序,而不是完整的模塊,它們不一定
抽象(隱藏)了什么東西。
通常大型的軟件系統包含太多的程序,這讓程序間使用關系的描述不太容易理解。在這
種情況下,使用關系可以用于程序的組合,如模塊、類或包。這樣的組合描述喪失了重
要的信息,但有助于展示“全局”。例如,你有時候可以在信息隱藏模塊之間建立使用
關系,但是除非一個模塊中所有的程序都屬于實際使用層次的同一層,否則就會喪失重
要的信息。
在某些項目中,系統的使用關系開始并沒有完全確定,要到系統實現時才能確定,因為開
發者會在實現過程中決定他們使用哪些程序。但是,系統的架構師可能在設計時創建一種
“允許使用”關系,約束開發者的選擇。今后,我們不會區分“使用”和“允許使用”。
定義良好的使用結構將創建系統的適當子集,可以用于驅動迭代式或增量式的開發循環。
滿足的關注點:產品化和生態系統。
1.3.3 進程結構
組件與關系:信息隱藏模塊結構和使用結構是靜態的結構,存在于設計時和編碼時。我
們現在轉向運行時結構。參與進程結構的組件是進程。進程是運行時的事件序列,由程
序控制(Dijkstra 1968)。每個程序都作為一個或多個進程的一部分執行。一個進程中的
事件序列的執行獨立于另一進程中的事件序列,除非這兩個進程彼此同步,例如一個進
程等待來自另一個進程的信號或消息。進程由支持系統分配資源,包括內存和處理器時
間。系統可能包含固定數量的進程,也可能在運行時創建和銷毀進程。請注意,在
架構概述25
InfoQ企業軟件開發叢書《架構之美》精選版
13
Linux和Windows操作系統中實現的線程也符合這個進程定義。進程是幾種不同關系中
的組件。下面是一些例子。
進程提供工作
一個進程可能會創建工作,該項工作必須由其他進程完成。這種結構在確定系統是否死
鎖時是很重要的。
滿足的關注點:性能和容量。
進程取得資源
在動態分配資源的系統中,一個進程可能控制由另一個進程使用的資源,后者必須請求
并歸還這些資源。因為發起請求的進程可能從幾個控制器那里請求資源,所以每項資源
可能都有一個不同的控制進程。
滿足的關注點:性能和容量。
進程共享資源
兩個進程可能共享資源,如打印機、內存或端口等。如果兩個進程共享一項資源,就需
要通過同步來防止使用沖突。每一種資源可能有不同的關系。
滿足的關注點:性能和容量。
進程包含在模塊中
每個進程由一個程序控制,正如前面提到的,每個程序包含在一個模塊之中。因此,我
們可以認為進程包含在模塊之中。
滿足的關注點:性能和容量。
1.3.4 訪問結構
系統中的數據可能劃分成具有屬性的段,如果程序對段中的任何數據擁有訪問權,就對
該段中的所有數據擁有了訪問權。請注意,為了簡化描述,我們應該讓段的規模最大化,
具體做法是添加一個條件,即如果兩個段被同一組程序訪問,這兩個段就應該合并。數
據訪問結構包含兩種類型的組件:程序和段。這種關系被命名“有權訪問”,它是程序
和數據段之間的關系。如果這種結構讓程序訪問的權限最小化,并且嚴格執行,我們就
認為系統更安全。
滿足的關注點:安全性。
1.3.5 結構小結
表1-1總結了前面的軟件結構,包括它們的定義和它們滿足的關注點。
26 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
14
表1-1:結構小結
結構組件關系關注點
信息隱藏信息隱藏模塊整體-部分可變性
包含模塊化
可構建性
使用程序使用產品化
生態系統
進程進程(任務、線程) 提供工作性能
取得資源可變性
共享資源容量
包含在模塊中
??
數據訪問程序和數據段有權訪問安全性
生態系統
1.4 好的架構
我們曾提到,架構師玩的是折中的游戲。對于一組給定的功能需求和品質需求,沒有唯
一的正確架構和唯一的“正確答案”。我們從經驗中得知,應該對架構進行評估,確定
它是否滿足其需求,然后再投入資金來構建、測試和部署這個系統。評估試圖回答前面
小節中討論的一個或多個一般關注點問題,或回答特定系統的具體關注問題。
架構評估有兩種常見的方式(Clements、Kazman和Klein 2002)。第一種評估方式是確
定架構的屬性,通常通過建?;蚰M系統的一個或多個方面。例如,通過性能建模來評
估吞吐量和伸縮性,通過失效樹模型來評估可靠性和可訪問性。其他類型的模型包括復
雜性和耦合指標,用于評估可變性和可維護性。
第二種評估方式,也是最廣泛使用的方式,就是通過對架構師提出質詢來評估該架構。
有許多結構化的質詢方法。例如,貝爾實驗室提出的軟件架構復查委員會(Software
Architecture Review Board,SARB)過程利用了組織機構之內的專家,以及他們在電信
和相關應用中的深厚領域經驗(Maranzano等2005)。
質詢方法的另一種變體是架構折中分析方法(Architecture Trade-off Analysis Method,
ATAM)(Clements、Kazman和Klein 2002),它尋找架構不能滿足品質關注點的風險。
ATAM使用了場景分析,每種場景都描述了特定的利益相關人對系統的品質關注點。架
構師然后解釋該架構如何支持每一種場景。
主動復審是另一種質詢方法,它改變了復審過程的開始方式,要求架構師向復審者提供
架構師認為重要而需要回答的問題(Hoffman和Weiss 2000,第17章)。然后,復查者利
架構概述27
InfoQ企業軟件開發叢書《架構之美》精選版
15
用已有的架構文檔和描述來回答這些問題。最后,在網絡上查找“software architecture
review checklist(軟件架構復審檢查清單)”,可以得到幾十份檢查清單,其中某些清單
非常通用,另一些則是針對具體的應用領域或技術框架。
1.5 美麗的架構
所有前面的方法都有助于我們判斷一個架構是否“足夠好”—也就是說,是否有可能
指導開發者和測試者構建一個系統,并滿足系統的利益相關人的功能和質量關注點。在
我們每天使用的系統中存在著許多好的架構。
但是,超越足夠好的架構是怎樣的呢?如果有一個“軟件架構名人堂”,那會怎樣?哪
些架構會陳列在這個藝術館的墻上?這個想法可能沒有你想象的那么遙遠—在軟件產
品線領域,這樣的“名人堂”的確存在。(注1)進入“軟件產品線名人堂”的條件包括
獲得商業上的成功、影響其他產品線的架構(其他產品線可能“借用、復制、竊取”這
個架構)、擁有足夠的文檔從而讓其他人“不必通過道聽途說”就能夠理解該架構。
我們將為更一般的“架構名人堂”或“美麗架構藝術館”的候選者設立怎樣的條件呢?
首先我們應該認識到,這是一個軟件系統的藝術館,而不是其他藝術館,我們的系統構
建的目的是為了使用。所以,我們也許從一開始就應該關注該架構的實用性:它應該每
天被許多人使用。
但是,在使用架構之前,它必須先構建,所以我們應該關注該架構的可構建性。我們會
尋找那些具有定義良好的使用結構的架構,它們支持增量式構建,這樣,通過每次構建
迭代我們都能得到一個有用的、可測試的系統。我們也會尋找那些具有定義良好的模塊
接口、本來就很好測試的架構,這樣,構建的過程就是透明的、可見的。
接來下,我們想尋找那些展示了持久性的架構—也就是說,那些經過了時間考驗的架
構。我們生活在一個技術環境以從未有過的加速度變化的年代。美麗的架構應該預期到
變更的需要,允許期望的修改能夠容易而有效地進行。我們想尋找那些避免了“衰老地
平線”(Klein 2005)的架構,超過了這條“衰老地平線”,維護將變得代價極大,以至
于不可能進行。
最后,我們還想尋找這樣一些架構,它們的特征讓使用、構建、測試這些架構的開發人
員和測試人員,以及由它而形成的系統的用戶感到由衷的高興。為什么開發人員會高
興?因為它讓他們的工作變得容易,而且更有可能得到一個高品質的系統。為什么測試
人員會高興?作為測試過程的一部分,他們必須嘗試模擬用戶的行為。如果他們高興,
28 第1章
注1: 參見http://www.sei.cmu.edu/productlines/plp_hof.html.
InfoQ企業軟件開發叢書《架構之美》精選版
16
可能用戶也會感到高興。如果廚師對他的烹調的菜品感到不高興,那么品嘗這些菜品的
顧客也可能會感到不高興。
不同的系統和應用領域為這些架構提供了許多機會,展示它們特有的令人高興的特征,但
概念完整性是一項跨越所有領域的特征,并且總是讓人感到高興。一致的架構學習起來更
容易、更快,當知道了一點之后,你就可以開始預測其余的部分。不需要記住并處理特殊
的情況,代碼更干凈,測試集更小。一致的架構不會為做同一件事情提供兩種(或更多)
方法,不會讓用戶浪費時間進行選擇。正如Ludwig Mies van der Rohe所說的,好的設計,
“少即是多”。愛因斯坦可能會說,美麗的架構就是盡可能簡單,但不要過于簡單。
有了這些判別條件,我們推薦第一批進入“美麗架構藝術館”的候選者。
第一個是A-7E艦載飛行處理器(Onboard Flight Processor,OFP)的架構,它由海軍研究實驗
室(Naval Research Laboratory,NRL)在20世紀70年代后期開發,在(Bass、Clements和
Kazman 2003)有介紹。盡管這個系統從未實現量產,但它滿足了所有其他的判別條件。這個
架構對軟件架構的實踐曾經產生了巨大的影響,它展示在真實世界的系統中,將設計時的
信息隱藏模塊和使用結構與運行時的進程結構分離。因為美國政府資助并開發了這個架構,
所以所有項目文檔都提供給了公共領域。(注2)這個架構具有定義良好的使用結構,促進
了系統的增量式構建。最后,信息隱藏模塊結構為分解系統提供了清晰一致的準則,實現
了很強的概念完整性。作為嵌入式系統軟件架構的榜樣,A-7E OFP當然屬于我們的藝術館。
我們想放入藝術館的另一個架構是朗訊5ESS電話交換機的軟件架構(Carney等1985)。
5ESS取得了全球范圍的商業成功,為世界各國的網絡提供了核心電話網絡交換。它成為
性能和可靠性的標準,每個單元每小時能處理超過100萬次的連接,平均每年非計劃宕機
時間少于10秒鐘(Alcatel-Lucent 1999)。該架構的一些統一概念,如管理電話連接的
“半通話模型”,已經成為電話和網絡協議領域的標準模式(Hanmer 2001)。除了保持必
須處理的通話類型的數目為2n(其中n是通話協議的數目)之外,半通話模式還在操作系
統的進程概念和電話的通話類型概念之間建立起了聯系,從而提供了簡單的設計原則,
引入了漂亮的架構一致性。在過去的25年中,開發團隊涉及多達3000個人,他們發展并
增強該系統?;谒纳虡I成功、持久性和影響,5ESS架構是我們藝術館的一件好藏品。
還有一個我們想放入美麗架構藝術館的系統,它就是萬維網(World Wide Web,WWW)
的架構。它由Tim Berners-Lee在CERN創建,在(Bass、Clements和Kazman 2003)中
有介紹。萬維網當然已經取得了商業上的成功,它轉變了人們使用因特網的方式。即使
創建了新的應用、引入了新的功能,它的架構仍然保持不變。該架構的整體簡單性促成
架構概述29
注2: 參見CHoffman和Weiss2000)的第6章、第15章和第16章,或在NRL Digital Archives
(http://torpedo.nrl.navy.mil/tu/ps)中查找“A-7E”。
InfoQ企業軟件開發叢書《架構之美》精選版
17
了它的概念完整性,但有一些決定導致了該架構的完整性保持不變,如客戶端和服務器
端使用同一個庫,創建分層架構以實現分離關注點等。核心萬維網架構的持久性和它對
新擴展、新功能持續支持的能力,使它當之無愧地進入了我們的藝術館。
什么是建筑師?
夏天很熱的一個日子里,一個外鄉人沿著一條路在行走。他走著走著,來
到一個人跟前,此人正在路邊敲碎石頭。
“你在做什么?”他問那個人。
那個人抬頭看著他;“我在敲碎石頭。你以為我看起來像在干什么?現在
不要妨礙我,讓我繼續干活?!?br /> 這個外鄉人繼續沿著路走,不久他遇到了第二個在大太陽下敲碎石頭的人。
這個人正努力工作,汗滴如雨。
“你在做什么?”外鄉人問道。
這個人抬頭看他,露出微笑。
“我在為謀生而工作,”他說,“但這個工作太辛苦了。也許你能給我一份更
好的工作?”
外鄉人搖了搖頭,繼續前行。沒多久,他遇到了第三個敲碎石頭的人。太
陽正是最炙熱的時候,這個人非常賣力,汗流如注。
“你在做什么?”外鄉人問道。
這個人停了一下,喝了一口水,微笑著抬起他的手,指向天空。
“我在建一座大教堂。”他喘著氣說。
外鄉人看了他一會兒,說:“我們正打算開一家新公司。你來做我們的總建
筑師怎么樣?”
我們的最后一個例子是UNIX系統,它展示了概念完整性,使用廣泛,擁有巨大的影響
力。管道和過濾器的設計是討人喜歡的抽象,允許我們快速構建新的應用。
在描述架構、架構師的角色和創建架構時的考慮等方面,我們已經談了很多,我們也簡
單介紹了一些美麗架構的例子。接下來我們邀請你閱讀后續章節中詳細的例子,這些例
子來自于那些技藝精湛的架構師,本書介紹了他們創建并使用過的那些美麗架構。
致謝
David Parnas在幾篇論文中定義了我們描述的許多結構,其中包括他的“術語濫用”論
文(Parnas 1974)。Jon Bentley為這本書提供了創作靈感,他和Deborah Hill、Mark
Klein對早期的草稿提出了許多有價值的建議。
30 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
18
參考文獻
Alcatel-Lucent. 1999. “Lucent’s record-breaking reliability continues to lead the industry
according to latest quality report.”Alcatel-Lucent Press Releases. June 2. http://www.alcatellucent.
com/ wps/ portal/ News Releases/ DetailLucent? LMSG_CABINET= Docs_and_ Resource_
Ctr&LMSG _CONTENT_FILE=News_Releases_LU_1999/LU_News_Article_007318.xml (accessed May 15, 2008).
Bass, L., P. Clements, and R. Kazman. 2003. Software Architecture in Practice, Second
Edition. Boston, MA: Addison-Wesley.
Blaauw, G., and F. Brooks. 1997. Computer Architecture: Concepts and Evolution. Boston,
MA: Addison-Wesley.
Booch, G., J. Rumbaugh, and I. Jacobson. 1999. The UML Modeling Language User Guide.
Boston, MA: Addison-Wesley.
Brooks, F. 1995. The Mythical Man-Month. Boston, MA: Addison-Wesley.
Carney, D. L., et al. 1985. “The 5ESS switching system: Architectural overview.” AT&T
Technical Journal, vol. 64, no. 6, p. 1339.
Clements, P., et al. 2003. Documenting Software Architectures: Views and Beyond. Boston,
MA: Addison-Wesley.
Clements, P., R. Kazman, and M. Klein. 2002. Evaluating Software Architectures. Boston:
Addison-Wesley.
Conway, M. 1968. “How do committees invent.”Datamation, vol. 14, no. 4.
Courtois, P. J. 1977. Decomposability: Queuing and Computer Systems. New York, NY:
Academic Press.
Dijkstra, E. W. 1968. “Co-operating sequential processes.”Programming Languages. Ed.
F. Genuys. New York, NY: Academic Press.
Garlan, D., and D. Perry. 1995. “Introduction to the special issue on software architecture.”
IEEE Transactions on Software Engineering, vol. 21, no. 4.
Grinter, R. E. 1999. “Systems architecture: Product designing and social engineering.”
Proceedings of ACM Conference on Work Activities Coordination and Collaboration
(WACC '99). 11-18. San Francisco, CA.
Hanmer, R. 2001. “Call processing.” Pattern Languages of Programming (PLoP). Monticello, IL.
http://hillside.net/plop/plop2001/accepted_submissions/PLoP2001/rhanmer0/PLoP2001_rhanmer0_1.pdf.
架構概述31
InfoQ企業軟件開發叢書《架構之美》精選版
19
Hoffman, D., and D. Weiss. 2000. Software Fundamentals: Collected Papers by David L.
Parnas. Boston, MA: Addison-Wesley.
IEEE. 2000. “Recommended practice for architectural description of software intensive
systems.” Std 1471. Los Alamitos, CA: IEEE.
Klein, John. 2005. “How does the architect's role change as the software ages?”
Proceedings of the 5th Working IEEE/IFIP Conference on Software Architecture (WICSA).
Washington, DC: IEEE Computer Society.
Maranzano, J., et al. 2005. “Architecture reviews: Practice and experience.” IEEE
Software, March/April 2005.
Parnas, David L. 1974. “On a buzzword: Hierarchical structure.”Proceedings of IFIP
Congress. Amsterdam, North Holland. [Reprinted as Chapter 9 in Hoffman and Weiss (2000).]
Waldo, J. 2006. “On system design.”OOPLSA '06. October 22-26. Portland, OR.
Weiss, D., and C. T. R. Lai. 1999. Software Product Line Engineering. Boston, MA:
Addison-Wesley.
32 第1章
InfoQ企業軟件開發叢書《架構之美》精選版
20
第2 章
兩個系統的故事:現代軟件神話
Pete Goodliffe
架構是一種很浪費空間的藝術。
—Philip Johnson
軟件系統就像一座由建筑和后面的路構成的城市—由公路和旅館構成的錯綜復雜的網
絡。在繁忙的城市里發生著許多事情,控制流不斷產生,它們的生命在城市中交織在一
起,然后死亡。豐富的數據積聚在一起、存儲起來,然后銷毀。有各式各樣的建筑:有
的高大美麗,有的低矮實用,還有的坍塌破損。隨著數據圍繞著它們流動,形成了交通
堵塞和追尾、高峰時段和道路維護。軟件之城的品質直接與其中包含多少城市規劃有關。
某些軟件系統很幸運,創建時由有經驗的架構師進行了深思熟慮的設計,在構建時體現
出了優雅和平衡,有很好的地圖,便于導航。另一些軟件系統就沒有這么幸運,基本上
是一些通過偶然聚集的代碼漸漸形成的,交通基礎設施不足,建筑單調而平凡,置身于
其中時會完全迷失,找不著路。
你的代碼愿意待在怎樣的“城市”中?你愿意構建哪一種城市?
在本章中,我將講述這樣兩個軟件城市的故事。這是真實的故事,就像所有好的故事一
樣,這個故事最終是有教育意義的。人們說經驗是偉大的老師,但最好是別人的經驗,如果
你能從這些項目的錯誤和成功之中學習,你(和你的軟件)可能會避免很多的痛苦。
本章中的這兩個系統特別有趣,因為它們有很大不同,盡管從表面上看非常相似:
33
InfoQ企業軟件開發叢書《架構之美》精選版
21
? 它們具有相似的規模(大約500 000行代碼)。
? 它們都是“嵌入式”消費音頻設備。
? 每種軟件的生態系統都是成熟的,已經經歷了許多的產品版本。
? 兩種解決方案都是基于Linux的。
? 編碼采用C++語言。
? 它們都是由“有經驗的”程序員開發的(在某些情況下,他們本應知道得更多)。
? 程序員本身就是架構師。
在這個故事中,人名都已改變,目的是保護那些無辜的人(和有罪的人)。
2.1 混亂大都市
你們修筑、修筑,預備道路,將絆腳石從我百姓的路中除掉。
—《以賽亞書》第57章14節
我們要看的第一個軟件系統名為“混亂大都市”。它是我喜歡回顧的一個系統—既不
是因為它很好,也不是因為它讓參與開發的人感到舒服,而是因為當我第一次參與它的
開發時,它教給了我有價值的軟件開發經驗。
我第一次接觸“混亂大都市”,是在我加入了創建它的公司時。初看上去這是一份有前
途的工作。我將加入一個團隊,參與基于Linux的、“現代”的C++代碼集開發,已有的
代碼集已經開發幾年了。如果你像我一樣擁有特殊的技術崇拜,就會覺得很興奮。
工作起初并不順利,但是你不能指望在加入一個新團隊、面對新的代碼集時會覺得很輕
松。然而,日復一日(周復一周),情況卻沒有任何好轉。這些代碼要花極長的時間來
學習,沒有顯而易見的進入系統中的路徑。這是個警告信號。從微觀的層面來說,也就
是從每行程序、每個方法、每個組件來看,代碼都是混亂而粗糙地壘在一起的。不存在
一致性、不存在風格、也沒有統一的概念能夠將不同的部分組織在一起。這是另一個警
告信號。系統中的控制流讓人覺得不舒服,無法預測。這又是一個警告信號。系統中有
太多的“壞味道”(Fowler 1999),整個代碼集散發著腐爛的氣味,是在大熱天里散發
著刺激性氣體的一個垃圾堆。這是一個清晰的警告信號。數據很少放在使用它的地方。
經常引入額外的巴羅克式緩存層,目的是試圖讓數據停留在更方便的地方。這又是一個
警告信號。
當我試圖在大腦中建立“大都市”的全圖時,沒有人能解釋它的結構;沒有人知道它的
所有層、它的藤蔓,以及那些黑暗、隔離的角落。實際上,沒有人知道它究竟有多少部
分是真正能工作的(它實際上靠的是運氣和英雄式的維護程序員)。人們知道他們面對
34 第2章
InfoQ企業軟件開發叢書《架構之美》精選版
22
的那一小部分區域,但沒人了解整個系統。很自然,沒有任何文檔。這也是一個警告信
號。我需要的是一份地圖。
這是一個悲傷的故事,我曾是其中的一部分:“大都市”是城市規劃的惡夢。在你開始
整治混亂之前,先要理解混亂,所以我們花了很大的精力和毅力,得到了一份“架構圖”。
我們標出了每一條公路、每一條主干道、每一條很少人了解的小路、所有燈光昏暗的輔
路,并將它們畫在一張主圖上。我們第一次看到了這個軟件的樣子,并不令人賞心悅目。
它是一些混亂的區塊和線條。為了讓它更好理解一些,我們用顏色標出了控制路徑,突
出了它們的類型。然后我們后退一步看著它。
它令人吃驚。它令人目眩神迷。它就像一只喝醉了的蜘蛛,跌進了一些海報顏料罐里,
然后在一張紙上織成了一張彩色的網。它看起來就像圖2-1那樣(這是一個簡化后的版
本,細節已經修改了,為了保護那些有罪的人)。事情變得很清楚了。我們畫出了倫敦
地鐵圖。它甚至有環線。
圖2-1:“混亂大都市”的“架構”
這就是那種讓跑遍各地的銷售員惱怒的系統。實際上,它與倫敦地鐵的相似性讓人印象
深刻:從系統的一端到另一端有很多條路線,哪條路最好通常是不明顯的。地理位置很
近的目的地常常很難到達,你希望能在兩點之間再挖掘一條隧道。有時候,走出地鐵換
乘巴士實際上是更好的選擇?;蛘吒纱嗖叫小?br /> 無論從哪個角度來看,這都不是一個“好的”架構。“大都市”的問題超出了設計的范
疇,它涉及開發過程和企業文化。這些問題實際上導致了許多架構腐爛。代碼經過幾年
的“有機”生長,沒有人進行過任何架構設計,而且各個部分是隨著時間推移,沒有經
過太多思考就栓在一起的。我們這么說真的算是客氣的了。沒有人停下來為代碼建立一
兩個系統的故事:現代軟件神話35
InfoQ企業軟件開發叢書《架構之美》精選版
23
個理智的結構。它增長、膨脹,成為絕對沒有任何架構設計的系統的一個典型。代碼集
從來不會沒有架構。這個系統只是擁有一個很糟糕的架構。
如果我們回顧創建“大都市”的公司的歷史,它所處的狀態是可以理解的(但是不可寬
恕):這是一個初創的公司,快速提供許多新版本的壓力很大。延期是不可容忍的—
這會帶來財務災難。軟件工程師被迫盡其極限,快速交付。所以代碼是以一系列瘋狂沖
刺的方式壘在一起的。
注意:不好的公司結構和不健康的開發過程將在糟糕的軟件架構中得到反映。
2.1.1 后果
“大都市”缺少城市規劃,這帶來了許多后果,我們將在這里進行分析。這些后果的影響是
很嚴重的,遠遠超出了你對不良設計的天真想象。地鐵變成了云宵飛車,飛速地朝下猛沖。
不可理解
正如你已經看到的,“大都市”的架構以及缺乏強制的結構,導致了一個很難理解的軟
件系統,實際上幾乎不可能修改。新加入項目的團隊成員(譬如我)會被復雜性驚呆,
不能夠搞清楚狀況。
壞的設計確實會招致在它上面疊加壞的設計(實際上它簡直就是迫使你那樣做),因為沒有
一種明智的方式可以擴展該設計。在所有能解決手上工作的方法之中,阻力最小的總會被
采用,沒有明顯的辦法來修復這些結構問題,所以只要能減少麻煩,就會扔進去新的功能。
注意:重要的是要保持軟件設計的品質。壞的架構設計會招致更壞的架構設計。
缺乏內聚
系統的組件完全沒有內聚性。每個組件本來都應該有一個定義良好的角色,但是它們卻
包含了一堆雜亂的、不一定相關的功能。這使我們很難確定組件存在的原因,也很難弄
明白系統中已經實現了哪項具體的功能。
很自然,這讓缺陷修復成為了一場噩夢,嚴重地影響了軟件的品質和可靠性。
功能和數據都放在了系統中錯誤的地方。許多你認為是“核心服務”的部分卻沒有在系
統的核心部分實現,而是由邊遠的模塊來模擬實現(非常痛苦并且代價很大)。
進一步的軟件歷史考察揭示了原因:原來的團隊中存在個人斗爭,所以一些關鍵程序員
開始創建他們自己的軟件小帝國。他們把自己認為酷的功能放到他們的模塊中,即使它
不應該屬于那里。為了做到這一點,他們于是又實現了更為巴羅克式的通信機制,把控
36 第2章
InfoQ企業軟件開發叢書《架構之美》精選版
24
制連回到正確的地方。
注意:開發團隊中健康的工作關系將直接有益于軟件設計。不健康的關系和個性膨脹會導致不健
康的軟件。
內聚和耦合
軟件設計的關鍵品質是內聚和耦合。這不是什么新奇的“面向對象”概
念;自從20世紀70年代出現結構化設計開始,開發者對這一概念已經談論
了許多年。我們的目標是通過設計使系統的組件具備下列品質:
? 高內聚(Strong cohesion)
內聚是一個測量指標,說明相關的功能如何聚集在一起,模塊內的各部分
作為一個整體工作得如何。內聚性是將模塊粘成一個整體的膠水。
弱內聚的模塊是不良分解的信號。每個模塊都必須具有清晰定義的角色,
而不只是一堆不相關的功能。
? 低耦合(Low coupling)
耦合是模塊之間獨立性的測量指標—它們之間進出“電線”的數量。在
最簡單的設計中,模塊幾乎沒有什么耦合,所以彼此間的依賴關系較少。
顯然,模塊不能夠完全解耦,否則它們將根本不能夠一起工作!
模塊之間的聯系有多種方式,有的是直接的,有的是間接的。模塊可以調
用其他模塊中的函數,或被其他模塊所調用。它可能使用其他模塊提供的
Web服務或設施,可能使用其他模塊的數據類型,或提供某些數據讓其他模
塊使用(可能是變量或文件)。
好的軟件設計會限制通信的線路,只提供那些絕對需要的。這種通信線路
是確定架構的一部分因素。
不必要的耦合
“大都市”沒有清晰的分層。模塊之間的依賴關系不是單向的,耦合常常是雙向的。組
件A會到達組件B的內部,目的是完成它的一項任務。在其他的地方,組件B又通過硬編
碼調用了組件A。系統沒有最底層,也沒有控制中心。它是整體式的一大塊軟件。
這意味著系統的各部分之間耦合非常緊密,你想啟動系統骨架就不得不創建所有的組件。
單個組件的任何改變都會波及其他組件,需要修改許多依賴它的組件。孤立地看代碼組
件沒有任何意義。
兩個系統的故事:現代軟件神話37
InfoQ企業軟件開發叢書《架構之美》精選版
25
這使得低層次的測試不能夠進行。不僅是代碼層次的測試不可能進行,而且組件層次的
集成測試也不能夠創建,因為每個組件都依賴于幾乎所有其他組件。當然,在公司中,
測試從來也不具有很高的優先級(我們根本沒有時間來做這種測試),所以這“不成為
問題”。不必說,這個軟件不太可靠。
注意:好的設計考慮到內部組件連接的連接機制和連接數(以及連接性質)。系統的單個部分應該
能夠獨立存在。緊耦合將導致不可測試的代碼。
代碼問題
不良的頂層設計所帶來的問題也影響到了代碼層面。問題會引起其他問題(參見Hunt
和Davis[1999]中關于破窗理論的討論)。因為沒有通用的設計,也沒有整體項目“風格”,
所以也沒有人關心共同的編碼標準、使用共同的庫,或采用共同的慣例。組件、類和文
件都沒有命名慣例。甚至都沒有共同的構建系統。膠帶、Shell腳本、Perl膠水與
makefile和Visual Studio項目文件混在一起。編譯這個怪物被視為一場復雜的成人儀式!
“大都市”最微妙而又最嚴重的問題是重復。由于沒有清晰的設計,也不清楚功能應該
處于的位置,所以輪子在整個代碼集中不斷重新發明。一些簡單的東西,如通用算法和
數據結構,在許多模塊中重復出現,每種實現都帶有自己的一些未知的缺陷和怪異的行
為特征。更大范圍的關注點,如外部通信和數據緩存,也實現了許多次。
更多的軟件歷史考察揭示了原因:“大都市”開始是從一系列獨立的原型拼起來的,這
些原型本該拋棄?!按蠖际小睂嶋H上是偶然形成的城市群。當代碼組件縫合在一起時,
組件之間匹配得不好。隨著時間的推移,這種隨意的縫合開始破裂,所以組件互相拉扯,
導致代碼集破碎,而不是和諧地協作。
注意:松弛而模糊的架構將導致每個代碼組件編寫得不好,并且相互之間匹配得不好。它也會導
致重復的代碼和工作。
代碼以外的問題
“大都市”內部的問題已經超越了代碼集,在公司中其他的地方導致了混亂。不僅開發
團隊中有問題,而且架構的腐爛也影響到了支持和使用該產品的人。
開發團隊
項目的新成員(例如我)被復雜性驚呆了,不能夠搞清楚狀況。這很好地解釋了為
什么很少有新人能在公司里待下來—員工流失率非常高。
38 第2章
InfoQ企業軟件開發叢書《架構之美》精選版
26
那些留下來的人非常努力地工作,項目的壓力非常大。規劃新的功能會導致極大的
恐懼。
緩慢的開發周期
由于維護“大都市”是一項恐怖的任務,所以即使是最簡單的變更或“很小的”缺
陷修復都不知道要花多少時間。管理軟件開發周期非常難??蛻糁缓玫戎鴮崿F重要
的特征,管理層對開發團隊不能滿足業務目標感到越來越沮喪。
支持工程師
在支持這個極不尋常的產品時,產品支持工程師度過了可怕的時光,他們要設法弄
明白很小的軟件版本差異之間錯綜復雜的行為差異。
第三方支持
項目開發了一個外部支持協議,支持其他設備遠程控制“大都市”。由于它只是軟
件內部結構上面薄薄的一層,所以它反映了“大都市”的架構,這意味著它也是巴
羅克式的、難以理解的、容易偶爾出錯的、不可能使用的。第三方工程師的生活也
被“大都市”的可怕結構搞得一團糟。
公司內部政治
開發問題導致了公司內部不同“種族”的分裂。開發團隊與營銷銷售團隊之間關系
緊張,每次新版本要推出時,制造部門總是要承受巨大的壓力。經理們已經絕望了。
注意:不良架構的影響不僅限于代碼。它會進一步影響到人、團隊、過程和時間表。
清晰的需求
軟件歷史考察凸顯了“混亂大都市”之所以混亂的一個重要原因:在項目開始之初,團
隊并不知道要構建的是什么。
本來的初創公司知道它要占領哪個市場,但不知道哪種產品能占領這個市場。所以他們兩
面下注,要求一個可以做許多事情的軟件平臺。噢,我們昨天就想得到它了。所以程序員
們急急忙忙創建了一個毫無希望的總體基礎設施,它具有做任何事情的潛力(但做得不好),
而不是創建一個把一件事情做好的架構,并能夠在將來進行擴展,做更多的事情。
注意:重要的是要在開始設計系統之前知道你打算設計什么。如果你不知道它是什么,也不知道
它將做什么,暫時不要開始設計它。只設計你知道需要的東西。
在規劃“大都市”的早期階段,有太多的架構師。面對糊涂的需求,他們都拿著一塊拼
兩個系統的故事:現代軟件神話39
InfoQ企業軟件開發叢書《架構之美》精選版
27
不起來的拼圖,試圖獨自工作。他們在工作時沒有考慮到整個項目,所以當他們試圖將
這些拼圖拼在一起時,就拼不起來了。沒有時間進一步思考架構,軟件設計的各個部分
有一些重疊,于是開始了“大都市”的城市規劃災難。
2.1.2 現狀
“大都市”的設計幾乎完全是無可救藥的—相信我,隨著時間的推移,我們也嘗試過
修復它。修復工作需要返工、重構、修改代碼結構中的問題,這些已經成為不可能的任
務。重寫也不是省事的方案,因為支持老的、巴羅克式的控制協議是需求的一部分。
你可以看到,“大都市”的“設計”產生的后果是殘酷的,并且會無情地變得更糟。很難
加入新的特性,所以人們只是忙著添加更多不完善的功能、救急補丁和編造的謊言。沒
有人在面對代碼時感到愉快,項目正盤旋著向下栽。缺乏設計導致了不良的代碼,從而
又導致了不良的團隊精神和不斷變長的開發周期。這最終導致了公司嚴重的財務問題。
最后,管理層宣布“混亂大都市”已經不盈利了,它被拋棄了。對于任何組織機構來說,
這都是勇敢的一步,特別是這個公司一直都眼高手低,同時又試圖避免沉淪。帶著團隊
從以前版本中得到的所有C++和Linux經驗,他們在Windows上用C#重寫了系統。猜猜
看會怎么樣。
2.1.3 來自“大都市”的名信片
那么我們學到了什么?不良的架構會產生深遠的影響和嚴重的反彈。在“混亂大都市”
中缺少預見性和架構設計,導致了下面的問題:
? 低品質的軟件和漫長的版本發布周期。
? 系統沒有彈性,不能夠適應變更或添加新的功能。
? 無處不在的代碼問題。
? 員工問題(壓力大、士氣低、跳槽等)。
? 大量混亂的公司內部政治。
? 公司不能成功。
? 許多痛苦和面對代碼深夜加班。
2.2 設計之城
形式永遠服從功能。
—Louis Henry Sullivan
“設計之城”軟件項目表面上與“混亂大都市”非常相似。它也是用C++寫的消費音頻
40 第2章
InfoQ企業軟件開發叢書《架構之美》精選版
28
產品,運行在Linux操作系統上。但是,它的構建方式有很大不同,所以內部結構也非
常不同。
我從一開始就參加了“設計之城”項目。我們用有能力的開發者組成了一個全新的團隊,
從頭開始構建這個產品。團隊很小(開始有4名程序員),像“大都市”項目一樣,團隊
的結構是扁平的。幸運的是,沒有出現“大都市”項目中的個人對抗,在團隊中也沒有
出現任何爭權奪利的事。在此之前,團隊成員之間并不非常熟悉,不知道我們在一起可
以配合得多好,但我們對這個項目都很熱心,喜歡這項挑戰。
這樣很好。
Linux和C++是項目早期的決定,這項決定確定了團隊成員的組成。從一開始,項目就
有清晰定義的目標:具體的首個產品和將來功能的路線圖,代碼集必須能夠支持這些功
能。這將是一個通用目標的代碼集,可以適用于多種產品配置。
開發過程采用了極限編程(XP)(Beck和Andres 2004),很多人相信這種開發過程避開
了設計:直接開始編碼,不要想太遠。實際上,一些旁觀者對我們的選擇感到震驚,并
預言項目將以淚收場,就像“大都市”一樣。但這是一種常見的誤解。XP沒有貶低設計,
它貶低的是不必要的工作(即YAGNI原則,You Aren’t Going To Need It)。但是,如果
需要前端設計,XP就要求你進行設計。它也鼓勵使用快速原型(所謂的“spike”),快
速展現并驗證設計的有效性。這些都非常有用,對最終的軟件設計產生了極大的影響。
2.2.1 設計之城的第一步
在設計過程的早期,我們確定了主要的功能領域(這包括核心的音頻通道、內容管理和
用戶控制/界面)。我們考慮了它們如何在系統中適配,推出了初步的架構,包括了實現
性能需求所必需的核心線程模型。
系統中各獨立部分的相對位置關系體現為傳統的分層結構,圖2-2展示了簡化后的結果。
請注意,這并不是龐大的前端設計。它是有意為之的“設計之城”的簡單概念模型:圖
中只有一些大塊,這是一個基本的系統設計,可以隨著功能模塊的添加而輕松地增長。
雖然很基本,但這個初始架構為增長提供了堅實的基礎?!按蠖际小睕]有總體規劃,在
“方便”的地方嫁接(或修補)功能。
我們在系統的核心上花了額外的設計時間:音頻通道。它實際上是整個系統的一個內部
子架構。為了確定它,我們考慮了穿過一系列組件的數據流,最后得到了一個“過濾器
和管道”音頻架構,如圖2-3所示。根據產品的不同物理配置,它包含了這樣一些管道。
同樣,開始時這些管道只是一個概念,即圖中的一些方塊。我們當時還沒有決定如何將
所有模塊拼裝在一起。
兩個系統的故事:現代軟件神話41
InfoQ企業軟件開發叢書《架構之美》精選版
29
圖2-2:“設計之城”的初始架構
圖2-3:“設計之城”的音頻管道
我們在早期也選擇了項目將采用的支持庫(例如,可以從http://www.boost.org獲得的
Boost C++庫和一組支持數據庫的庫)。關于一些基本關注點的決定是這時候做出的,目
的是確保代碼能夠容易而一致地增長,這些決定包括:
? 頂層文件結構。
? 我們如何對事物命名。
? “內部”展示的風格。
? 共用的編碼慣例。
? 選擇單元測試框架。
? 支持基礎設施(例如版本控制、適合的構建系統和持續集成)。
這些“細節”完美的因素非常重要:它們與軟件架構密切相當,影響到后來的許多決定。
2.2.2 故事展開
在團隊完成了初始設計之后,“設計之城”項目按照XP過程推進。設計和編碼要么以結
對的方式完成,要么經過仔細的復審,確保工作的正確性。
42 第2章
用戶界面
控制組件
音頻通道
OS/音頻編解碼
音頻文件音頻硬件
InfoQ企業軟件開發叢書《架構之美》精選版
30
隨著時間的推移,設計和代碼不斷發展和成熟;隨著“設計之城”的故事逐漸展開,產
生了下面的結果。
定位功能
由于從一開始我們就有系統結構的清晰總體視圖,所以新的功能單元可以一致地添加到
代碼集的正確功能區域。代碼應該屬于哪一塊從來就不是問題。在擴展功能或修復問題
時,我們總是很容易找到已有功能的實現代碼。
現在,把新的代碼放到“正確”的位置有時候比簡單“嫁接”到方便而不妥的地方而更
難一些。所以,架構規劃的存在有時候讓開發者的工作變得更難一些。這些額外工作的
回報就是今后的生活要容易很多,當我們維護或擴展系統時,不愉快的事情會很少。
注意:架構有助于定位功能:添加功能、修改功能或修復缺陷。它為你提供了一個模板,讓你將
工作納入到一張系統導航圖中。
一致性
整個系統是一致的。各個層次的所有決定都是在整個設計的背景下做出的。開發者從一
開始就有意為之,這樣得到的所有代碼都完全符合系統設計,并與編寫的所有其他代碼
相匹配。
在項目的歷史中,盡管有許多變更,涉及代碼集的各處(從單行代碼到系統結構),但
這些變更都符合最初的設計模板。
注意:清晰的架構設計將導致一致的系統。所有決定都應該在架構設計的背景下做出。
頂層設計的好風格和優雅很自然會為較低的層帶來好處。即使在最低層,代碼也是統一
而整潔的。清晰定義的軟件設計確保了沒有重復,熟悉的設計模式到處使用,熟悉的接
口慣例普遍采用,沒有特殊的對象生命周期或奇怪的資源管理問題。代碼是在城市規劃
的背景之中寫成的。
注意:清晰的架構有助于減少功能重復。
架構的增長
有一些全新的功能領域出現在了設計“全圖”中,例如存儲管理和外部控制功能。在
“大都市”項目中,這是致命的一擊,難度超乎想象。但在“設計之城”項目中,事情
就不一樣了。
兩個系統的故事:現代軟件神話43
InfoQ企業軟件開發叢書《架構之美》精選版
31
系統設計就像代碼一樣,被認為是可擴展、可重構的。開發團隊的一項核心原則就是保
持敏捷,沒有什么是一成不變的,所以在需要時架構也可以修改。這促使我們讓設計保
持簡單并易于修改。這樣一來,代碼可以快速地增長,同時又保持好的內部結構。添加
新的功能塊不是問題。
注意:軟件架構不是一成不變的。需要時就改變它。要想做到可以修改,架構就必須保持簡單。
犧牲簡單性的修改要抵制。
延遲設計決定
有一項XP原則確實提高了“設計之城”的架構品質,這就是YAGNI(如果你不是馬上
需要,就不要去做)。這促使我們在早期只設計了重要的部分,將所有余下的決定推遲,
直到我們對實際的需求有了更清晰的理解并知道如何放到系統中最好時,再做出這些決
定。這是一種非常強大的設計方法,在很大的程度上解放了我們的思想。
? 當你還不理解問題時就開始設計,這是一件糟糕的事。YAGNI迫使你等待,直到
你知道真正的問題是什么,它應該怎樣由設計來體現為止。這消除了猜測的工作,
確保設計是正確的。
? 當你開始創建軟件設計時就加入所有可能需要的東西(包括廚房水槽)是危險的。
你的大部分設計工作會變成無用功,得到的只是額外的負擔,你不得不在軟件的
整個變更生命周期中支持這些設計。它一開始就增加了成本,而且在項目的生命
周期中不斷增加成本。
注意:延遲設計決定,直到你必須做出這些決定為止。不要在你還不知道需求的時候就做出架構
決定。不要猜測。
保持品質
從一開始,“設計之城”就準備好了一些品質控制過程:
? 結對編程。
? 對沒有結對編程的工作進行代碼/設計復審。
? 對每一段代碼進行單元測試。
這些過程確保了系統中從未加入不正確的、不合適的變更。所有不符合軟件設計的內容
都被拒之門外。這可能聽起來有點過于嚴厲,但這些是開發者們堅信的過程。
這種信念凸顯了一個重要的態度:開發者們相信設計,認為設計對項目相當重要。他們
擁有設計,對設計負責。
44 第2章
InfoQ企業軟件開發叢書《架構之美》精選版
32
注意:必須保持架構品質。只有當開發者們相信它并對它負責時,才能做到這一點。
管理技術債務
除了這些品質管理方法之外,“設計之城”的開發是相當注重實效的。隨著最后期限的臨近,
一些不太重要的功能被砍掉,讓產品能夠準時推出。小的代碼“瑕疵”或設計問題允許存在
于代碼集中,要么是為了讓功能快一點實現,要么是為了在接近發布時避免高風險的改動。
但是,與“混亂大都市”項目不同的是,這些逃避職責的地方被標記為技術債務,并安
排在后續的版本發布中修正。這些問題很清楚,開發者對它們不滿意,直到將它們處理
掉為止。同樣,我們看到了開發者對設計的品質負責。
單元測試打造了設計
關于代碼集的一項核心決定就是所有代碼都要有單元測試(這也是在XP開發中強制要求做
到的)。單元測試帶來了許多好處,其中一點就是能夠修改軟件的一些部分,而不必擔心在
修改的過程中破壞其他的東西。我們對“設計之城”內部結構的某些部分進行了相當激進的
返工,單元測試給了我們信心,讓我們相信系統的其他部分沒有被破壞。例如,線程模型和
音頻管道的內部連接接口都進行了徹底的改變。這是在子系統開發較晚的時候發生的嚴重設
計變更,但與音頻通道接口的其他代碼仍然執行得很好。單元測試讓我們能夠改變設計。
隨著“設計之城”的逐漸成熟,這種類型的“主要”設計變更越來越少了。在經過一些
設計返工之后,情況穩定下來,此后只有一些不重要的設計變更。系統開發得很快:以
迭代的方式進行,每一次迭代都改進了設計,直到它達到了相對穩定的狀態。
注意:你的系統應該有一組不錯的自動化測試,它們讓你在進行根本的架構變更時風險最小。這
為你提供了工作的空間。
單元測試的另一個主要好處在于,它們在很大程度上定型了代碼設計:它們實際上迫使
我們實現好的結構。每個小的代碼組件都被定型成定義良好的實體,可以獨立存在,因
為它必須能夠在單元測試中構造出來,不需要圍繞它構造系統的其他部分。編寫單元測
試確保了每個代碼模塊的內聚性,也確保了與系統其他部分之間的松耦合。單元測試迫
使我們仔細考慮每個單元的接口,確保該單元的API是有意義的,內部是一致的。
注意:對你的代碼進行單元測試將帶來更好的軟件設計,所以設計時要考慮可測試性。
設計時間
“設計之城”成功的另一個因素是分配的開發時間段,它既不長也不短(就像金發歌蒂
的粥,既不熱也不冷,剛剛好)。項目需要一個有利的環境才能獲得成功。
兩個系統的故事:現代軟件神話45
InfoQ企業軟件開發叢書《架構之美》精選版
33
如果時間太多,程序員常常會想創建他們的巨作(那種總是快要好了,但永遠不會實現
的東西)。有一點壓力是好事,緊迫感有助于完成事情。但是,如果時間太少,就不可
能得到任何有價值的設計,你只會得到半生不熟的解決方案,就像“大都市”那樣。
注意:好的項目計劃將帶來優質的設計。分配足夠的時間來創建架構杰作,它們不會立即出現。
與設計同行
盡管代碼集很大,但它是一致而易于理解的。新的程序員可以比較容易地拿起代碼并開
始工作。不需要去理解不必要的復雜內部關系,也不需要面對奇怪的遺留代碼。
由于代碼中產生的問題比較少,工作起來有樂趣,所以團隊人員的流失率很低。這是因
為開發者們負責設計,并不斷希望改進它。
看著開發團隊動態地遵守架構設計是一件有趣的事情?!霸O計之城”的項目原則規定沒有
人“擁有”哪一部分設計,這意味著任何開發者都可以改動系統的所有地方。每個人都應
該寫出高品質的代碼?!按蠖际小笔窃S多不協作的、互相爭斗的程序創造的一團混亂,而
“設計之城”則是由密切合作的同事創建的一組干凈、一致、密切合作的軟件組件。在很
大程度上,Conway法則(注)反過來也生效,團隊的組織方式就像軟件的組織方式一樣。
注意:團隊的組織方式必然對它產生的代碼有影響。隨著時間的推移,架構也會影響到團隊協作
的好壞。當團隊瓦解時,代碼的交互就很糟糕。當團隊協作時,架構就集成得很好。
2.2.3 現狀
在一段時間之后,“設計之城”的架構如圖2-4所示。也就是說,它與最初的設計非常相
似,同時也包含了一些值得注意的變更。此外,它還包含了大量的經驗,證明這個設計
是正確的。健康的開發過程,小的、更善于思考的開發團隊,適當注意確保一致性,帶
來了極為簡單、清晰、一致的設計。這種簡單性為“設計之城”帶來了好處,得到了可
擴展的代碼和快速開發的產品。
在編寫本書時,“設計之城”項目已走過了3年。代碼集仍在使用,而且擴展出了一些成
功的產品。它還在開發、成長、擴展,還在每天發生變化。下一個月它的設計可能與這
個很不同,但也可能沒有不同。
我要澄清一點:這些代碼并不完美。有些地方存在著技術爭論,但是它們在整潔的背景
下顯得特別突出,會在將來得到解決。沒有什么是一成不變的,由于適應性強的架構和
靈活的代碼結構,這些問題都可以解決。幾乎所有東西都各就各位,因為架構很好。
46 第2章
注: Conway法則指出,代碼結構符合團隊的結構。簡而言之,“如果你讓4個小組開發一個編
譯器,就會得到一個4階段編譯器?!?br /> InfoQ企業軟件開發叢書《架構之美》精選版
34
圖2-4:“設計之城”的最終架構
2.3 說明什么問題
等那完全的來到,這有限的必歸于無有了。
—《哥林多前書》第13章10節
這個關于兩個軟件系統的簡單故事當然不是軟件架構的全面介紹,但我已展示了架構如
何對軟件項目產生深遠的影響。架構幾乎會影響所有與之相關的人和事,它決定了代碼
集的健康,也決定了相關領域的健康。就像一個繁榮的城市會為當地帶來成功和聲望,
好的軟件架構將幫助項目獲得發展,為依賴于它的人帶來成功。
好的架構是很多因素的結果,包括以下方面(但不限于此):
? 確實進行有意為之的前端設計。(許多項目甚至還沒開始,就因為這一點而失敗了。)
? 設計者的素質和經驗。(以前犯過一些錯誤是有幫助的,這能在下一次為你指出正
確方向!“大都市”項目肯定教會了我一些東西。)
? 在開發過程中,保持清晰的設計觀點。
? 授權團隊負責軟件的整體設計,而團隊也承擔起這一責任。
? 不要害怕改變設計:沒有什么是一成不變的。
? 讓合適的人加入到團隊中,包括設計者、程序員和經理,確保開發團隊的規模合
適。確保他們具有健康的工作關系,因為這些關系將不可避免地影響代碼的結構。
? 在合適的時候做出設計決定,當你知道所有必要信息時再做出決定。延遲那些暫
時不能做出的決定。
? 好的項目管理,以及合適的最后期限。
兩個系統的故事:現代軟件神話47
用戶界面
控制外部控制器
存儲管理音頻通道
OS/音頻編解碼
InfoQ企業軟件開發叢書《架構之美》精選版
35
2.4 輪到你了
絕不要失去神圣的好奇心。
—阿爾伯特·愛因斯坦
你正在讀這本書是因為你對軟件架構感興趣,而且你對改進自己的軟件感興趣。所以這
里就有一個極好的機會。對于你目前的軟件經驗,請考慮以下簡單的問題:
1. 什么是你看到過的最好的系統架構?
? 你怎么知道它是好的?
? 這個架構在代碼集之內和之外帶來了什么結果?
? 你從中學到了什么?
2. 什么是你看到過的最差的系統架構?
? 你怎么知道它是差的?
? 這個架構在代碼集之內和之外帶來了什么結果?
? 你從中學到了什么?
參考文獻
Beck, Kent, with Cynthia Andres. 2004. Extreme Programming Explained, Second Edition.
Boston, MA: Addison-Wesley Professional.
Fowler, Martin. 1999. Refactoring: Improving the Design of Existing Code. Boston, MA:
Addison-Wesley Professional.
Hunt, Andrew, and David Thomas. 1999. The Pragmatic Programmer. Boston, MA:
Addison-Wesley Professional.
48 第2章
InfoQ企業軟件開發叢書《架構之美》精選版
36
第3 章
伸縮性架構設計
Jim Waldo
3.1 簡介
在設計系統架構時,一個比較有趣的問題就是確保系統在伸縮時的彈性。隨著越來越多
的系統運行在網絡上或在互聯網上提供訪問,伸縮性正變得越來越重要。對于這樣的系
統,如果你希望誤差的范圍在幾個數量級以內,那么容量規劃的想法顯然是荒謬的。如
果你架起一個網站,然后它火了,你可能會突然發現有幾百萬的用戶訪問你的站點。同
樣容易出現的情況是,你架起了一個網站,卻發現沒有人感興趣,你投入的所有設備都
閑置著,消耗著能源和管理成本,浪費錢財(這同樣是一種災難)。在網絡世界里,一
個站點可以在幾分鐘內從其中一種狀態轉變成另一種狀態。
只要是將系統連接到網絡上,每個人都會遇到伸縮性問題,但是“大型多人在線游戲”
(MMO)和虛擬世界特別關注這一點。這些系統必須具備伸縮性,以滿足大量的用戶。
Web服務器的用戶常常讀取的是靜態的內容,而且彼此之間沒有交互,但MMO中的玩
家或虛擬世界中的居民則不同,他們既需要與所處的世界進行交互(這改變了世界的基
本信息),也需要彼此之間的交互。這些交互行為使得這類系統基礎設施的伸縮性問題
變得更復雜,因為用戶與系統的交互幾乎是獨立的(除了那些不獨立的情況),而且不
會讓世界的狀態改變太多。對于一個世界里的任意兩個參與者,他們在某個時刻進行交
互的幾率是非常小的。但是,幾乎所有玩家在所有時候都在與他人交互。結果是這種系
統并行程度非常高,但只有少數的交互是互相依賴的。
51
原則與特性結構
√ 功能多樣性模塊
√ 概念完整性√ 依賴關系
修改獨立性進程
自動傳播√ 數據訪問
可構建性
√ 增長適應性
熵增抵抗力
InfoQ企業軟件開發叢書《架構之美》精選版
37
由于這些系統所培育起來的文化,MMO和虛擬世界的伸縮性問題進一步復雜化了。
MMO和虛擬世界都源自于視頻游戲產品。這是一種從PC游戲和游戲機游戲傳統中成長
起來的文化,在這種傳統中,程序員會假定游戲運行在一臺獨立的機器或游戲機上。在
這樣的環境中,機器所有的資源都受游戲程序控制,程序的問題也限于單個用戶玩游戲
的情況(實際上,缺陷或奇怪的行為常常會被認為是游戲本身邏輯的一部分)。
這些游戲和編寫、出品、擴展它們的公司,都屬于娛樂行業。編寫游戲的團隊由一個出
品人領導,有劇本和背景故事。游戲的目標是刺激、打動人,最重要的一點,要好玩。
可靠性很好,但不一定必需。可擴展性是游戲的特性,讓游戲在升級時能夠加入新的故
事情節和主題,但可擴展性不是代碼的特性,不必讓代碼能以新的、不同的方式使用。
在線游戲和虛擬世界的興起將這種文化帶入了一個新的環境,在這個環境中,需求與企業
應用開發者所面對的類似。多個用戶通過網絡在服務器上交互時,由于一個玩家的意外動
作而導致的服務器崩潰將影響許多其他玩家。隨著這些世界發展出自己的經濟(有些經濟
與現實世界的經濟有關系),在線世界的穩定性和一致性就超出了一個游戲的要求。隨著
這些世界中玩家或居民的人數達到百萬級,伸縮的能力就成為了任何架構的首要需求。
Darkstar項目(本章后面將簡稱為Darkstar)是對這些游戲和虛擬世界創建者的需求挑戰
的回答。這個項目由Sun公司實驗室的一個研究小組承擔,它將在架構的伸縮性領域不
斷探索。這個項目特別有趣之處在于,它是針對MMO和虛擬世界的創建者,這些程序
員與我們(作為系統設計者)所熟悉的那些程序員相比,有著非常不同的需求。得到的
架構看起來似乎很眼熟,但如果你仔細查看,會發現它的不同之處,它與你的經驗有所
不同。得到的架構有著屬于它自己的美麗,同時它也是一堂實踐課,說明了不同的需求
如何改變你所想到的構建系統的方式。
3.2 背景
像一座建筑或一個城市的物理架構一樣,系統的架構必須適應環境,利用該架構創建的工件將
存在于該環境之中。對于物理架構來說,這個環境包括工作的歷史環境、它所處位置的氣候、
本地工人的技能、可以獲得的建筑材料,以及建筑的使用意圖。對于軟件架構,這個環境不僅
包括使用該架構的應用程序,也包括那些要使用該架構的程序員,以及由此受到的系統約束。
在創建Darkstar架構時,我們(注1)意識到的第一件事就是所有針對伸縮性而設計的架
構都需要包含多臺機器。我們不清楚,就算是最大的大主機系統是否能夠滿足今天的一
52 第3章
注1: 在提到Darkstar項目的架構開發時,我通常會說“我們”做了什么,而不是“我”做了
什么。這不僅是編輯意義上的“我們”。該架構的設計是一個協作項目,由Jeffrey
Kesselman、Seth Proctor和James Megquier發起,并經過Seth、James、Tim Blackman、
Ann Wollrath、Jane Loizeaux和我的努力發展到現在的狀態。
InfoQ企業軟件開發叢書《架構之美》精選版
38
些在線游戲的要求(例如《魔獸世界》,據報道它有500萬注冊用戶,幾十萬的同時在線
用戶)。就算單臺機器能夠處理這種負載,我們也不能在一開始就假定游戲會取得如此
成功,需要這樣的硬件投資。這在經濟上是不可行的。這種應用需要能夠從很小的系統
開始,然后隨著用戶數的增長而增加處理能力,最后隨著大家對游戲興趣的衰退而降低
處理能力。這與分布系統的特點相符,在分布式系統中,我們可以隨著請求增長而添加
(合理的小)機器,當請求下降時移走機器。所以我們從一開始就知道,總體架構必須
是一個分布式系統。
我們也知道系統利用了芯片架構的當前趨勢。MMO和虛擬世界(在較小程度上)曾經
針對伸縮性利用過摩爾定律。隨著處理器的速度倍增,可以創造的世界會在復雜度、豐
富程度和互動性方面倍增。沒有其他計算領域像游戲世界這樣探索過處理器速度的增長
所帶來的好處。為游戲而設計的個人計算機總是將CPU速度、內存和圖形性能推向極致。
游戲機更激進地將這些方面推向極致,它們包含的圖形系統遠遠超過了高端工作站上的
圖形系統,整個機器完全是為了游戲玩家的特殊需要而打造的。
芯片演進方面最近的變化是從不斷增加的時鐘速度轉為實現多核處理器,這已經對游戲
中能做的事情產生了影響。新芯片的設計目標不是將一件事做得更快,而是同時做多件
事。如果在芯片上運行的多項任務實際上可以同時執行,那么在芯片層面上引入并發執
行將帶來更好的總體性能。在不改變時鐘速度的情況下,4核的芯片應該能比單核的芯
片多做3倍的事情。實際上,這種增長不是呈線性的,因為系統的其他一些部分沒有以
同樣的方式實現并發。但是可以通過并發來實現系統總體性能的增長,而且制造這種并
發的芯片比制造增加時鐘速度的芯片要容易得多。
基于這一事實,MMO和虛擬世界應該是多核芯片和分布式系統的理想候選者。在MMO
或虛擬世界中發生的大多數事情就像在真實世界中發生的大多數事情一樣,與該世界中
發生的其他事情是無關的。玩家繼續他們的搜索或裝飾他們的房間。他們與怪物交戰或
設計衣服。即使他們與該世界中的另一個玩家或居民發生交互,也只是與該世界的很少
一部分居民發生交互。這正是令人為難的并行計算任務的特點,也正是多核和多機系統
應該擅長處理的那種任務。
盡管這些系統中的任務的并行度讓人為難,但為這樣的系統編程的程序員卻沒有接受過分
布式計算或并發編程方面的訓練,也沒有這方面的經驗。這是極為微妙的領域,即使是在
這個領域接受過訓練的人和對這些技術相當有經驗的人也會感到困難。要讓大多數游戲程
序員來開發高度并發的、分布式游戲服務器,就是要求他們做超出自己的專長和經驗的事。
3.2.1 首要目標
這樣的背景為我們確立了該架構的首要目標。對伸縮性的需求表明,系統應該是分布式
伸縮性架構設計53
InfoQ企業軟件開發叢書《架構之美》精選版
39
的、并發的,但我們需要為游戲開發者提供簡單得多的編程模型。簡而言之,目標就是
游戲程序員應該把該系統視為一臺單機,運行著一個線程,所有允許部署到多線程和多
計算機上的機制都應該由Darkstar項目的基礎設施來考慮。
在一般的情況下,對應用程序隱藏分布式和并發是不可能的。但MMO和虛擬世界不是一
般的情況。我們試圖實現的這種隱藏,其代價就是必需一種非常特別的、嚴格限制的編
程模型。幸運的是,這種模型恰好非常適合游戲服務器和虛擬世界已經采用的編程方式。
Darkstar項目要求的一般編程模型是反應式的,在這種編程模型中,游戲的服務器端寫
成了事件監聽器,監聽客戶端生成的事件(客戶端就是游戲玩家使用的機器,通常是一
臺PC或游戲機)。如果檢測到事件,游戲服務器就應該生成一項任務,這個任務是一個
短期的計算序列,包括操作虛擬世界中的信息,并與最初生成事件的客戶端或其他一些
客戶端進行通信。任務也可以由游戲服務器自己生成,要么是響應某些內部的變化,要
么是周期性地根據時間來生成任務。在這種情況下,游戲服務器可以在游戲或虛擬世界
中生成一些角色,這些角色是不受外部玩家控制的。
這種編程模型非常適合游戲和虛擬世界,但它也應用于一些企業級的架構中,如J2EE和
Web服務。之所以需要創建一個不同于這些企業計算機制的架構,是因為MMO和虛擬
世界存在的環境非常不一樣。這種環境幾乎剛好和經典企業環境相反,這意味著如果你
接受過企業環境方面的訓練,你知道的所有事情在這個新世界中幾乎都是錯的。
經典的企業環境可以描述為一個“瘦”客戶端連接到一個“胖”服務器(這個服務器
又常常連接到一個更“胖”的數據庫服務器)。服務器將保存客戶端需要的絕大部分信
息,在最理想的情況下,客戶端內存不多,沒有自己的硬盤,它是服務器的一個稱職的
顯示設備,絕大多數真正的工作在服務器上完成。
3.2.2 游戲世界
MMO和虛擬世界的環境始于一個非常胖的客戶端:它通常是頂級的PC、具有最強勁的
CPU、很大的內存,以及本身計算能力就很強的顯卡。它也可以是一臺游戲機,專門為
圖形密集的、高度交互的任務而設計。只要有可能,數據就會存放在這些客戶端,特別
是那些不會改變的信息,如地理信息、材質貼圖和規則集。服務器保持盡可能的簡單,
通常只保存非常抽象的世界表示和其世界中的實體的表示。而且,服務器的設計目標是
盡可能少地進行計算。絕大部分的計算留給了客戶端。服務器的真正工作是保存共享的
世界真實狀態,確保不同客戶端對世界的看法差異可以根據需要得到糾正。真實狀態需
要由服務器來保存,因為控制客戶端的玩家很有興趣讓他們的表現變成最強,所以可能
會受到誘惑,根據他們的喜好修改共享的真實(如果他們可以)。在一般情況下,如果
有可能,玩家就會作弊,所以服務器必須是共享真實的最終來源。
54 第3章
InfoQ企業軟件開發叢書《架構之美》精選版
40
MMO和虛擬世界的數據訪問模式也和企業中看到的情況有著很大的區別。企業中的一
般經驗法則是90%的數據訪問都是只讀的,大多數任務會讀取大量數據,然后再改寫少
量數據。在MMO和虛擬世界的環境中,大多數任務只訪問服務器上少量的狀態數據,
但在它們訪問的數據中,大約一半會被改寫。
3.2.3 延遲是敵人
但是,這兩種環境中最大的不同要追溯到用戶所做的事情的不同。在企業環境中,目標
是管理業務,如果總吞吐量得到改進,在處理中有一點延遲是可以接受的。在MMO和
虛擬世界的環境中,目標是開心,而延遲是開心的敵人。所以MMO或虛擬世界的基礎
設施需要圍繞著盡可能限定延遲的需求來設計,即便以吞吐量為代價也在所不惜。
在線游戲和虛擬世界顯然已經找到了辦法來實現伸縮性,以應對數量巨大的用戶。目
前的方法可以分成兩大類。第一類實質上是基于地理位置來實現的。游戲設計成包含
一組不同的區域,每個區域運行在一臺服務器上。它可能是虛擬世界的一個島或房間,
也可能是在線游戲中的一個小鎮或山谷。游戲設計試圖讓每個區域無關,限定地理區
域的大小,這樣服務器不會因太多用戶進入這個區域而擁塞。在實踐中,這樣的區域
常常能實現自我限制,因為當服務擁塞時,游戲就會變得響應比較慢,趣味性下降。
因此,玩家就會轉向更有趣的區域,這使得以前擁塞的區域人數減少,響應時間得到
改進。
將不同地理區域分配給不同服務器來實現伸縮性的方法有一個問題,即必須在游戲編寫
時決定哪些區域應該放到一臺服務器上。雖然在游戲或虛擬世界中添加新的區域相當容
易,但是改變已經分配給服務器的區域卻可能需要改動代碼。決定哪些區域組成一個伸
縮性單位,這必須是開發工作的一部分。
第二種處理游戲或虛擬世界中擁塞區域的方法叫做分區(sharding)。一個分區是該區域
的一份副本,運行在它自己的服務器上,獨立于其他的分區,它代表了游戲中相同的部
分,即原來的區域。這樣,分區可能代表了某個房間或村莊的不同副本,允許成倍的玩
家進入到世界的這個部分中。分區的缺點是它們不允許處于不同分區的玩家彼此之間進
行交互。隨著游戲和虛擬世界變成更多的社交體驗,而不僅僅是玩游戲,這種缺點就明
顯了。玩家的目標不只是要出現在虛擬世界中,而是要和他們的朋友(真實的和虛擬的)
一起進入虛擬世界。分區阻礙了這個目標的實現。
因此,Darkstar架構的另一個主要目標就是支持隨時伸縮,同時不要求游戲邏輯受到伸
縮的影響。這個架構應該支持游戲動態地響應負載,而不是讓這種響應成為游戲設計工
作的一部分。
伸縮性架構設計55
InfoQ企業軟件開發叢書《架構之美》精選版
41
3.3 架構
Darkstar由一組獨立的服務構成,這些服務可以在游戲或虛擬世界的服務器端的地址空
間內獲得。每個服務都定義為一個小的編程接口。盡管不是出于本意,但Darkstar項目
提供的一些基本服務很像經典操作系統的服務,它們支持游戲或虛擬世界的服務器端訪
問持久存儲,調度并執行任務,與游戲或虛擬世界的客戶端進行通信。
用一組相互聯系的服務來構建這個系統,顯然是開始了“分而治之”的過程,分而治之
是設計所有大型計算機系統的基本方法。每種服務都可以用一個接口來描述,這讓使用
該服務的程序不會受到底層實現變更的影響,同時也支持這些實現可以獨立地完成。對
一個服務的實現進行變更不應該影響到另一個服務的實現,即使其他的服務會利用到這
個變更的服務(假定接口和接口的語義沒有變更)。
我們采用服務分解的方法還有其他的原因。從一開始,Darkstar項目就設計成一個開放
源代碼的項目,希望我們能夠放大核心團隊的工作,支持其他社區成員創建更多的服務,
豐富核心的功能。維護一個開源社區在任何情況下都是復雜的,我們相信在組成架構的
服務之間擁有最大程度的隔離,將支持在不同服務實現層次之間的更高級的隔離。此外,
當時并不清楚是否存在單一一組服務能夠適合所有的MMO和虛擬世界。將基礎設施設
計為一組獨立的服務,使得這些服務的不同組合可以在不同的情況下使用,這由使用該
基礎設施的具體項目的需求來決定。Darkstar棧中具體包含哪些服務可以由一個配置文
件來設置。
3.3.1 宏觀結構
圖3-1展示了基于Darkstar項目基礎設施的游戲或虛擬世界的基本結構。一些服務器構成
了游戲或虛擬世界的后端。每個服務器運行著一組選定服務的副本(稱為Darkstar棧)
和游戲邏輯的副本。客戶端將連接到其中一個服務器,與服務器保存的該世界的抽象表
示進行交互。
與大多數的復制策略不同,游戲邏輯的不同副本不需要處理相同的事件。每個副本可以
獨立地與客戶端進行交互。在這個設計中,復制主要用于支持伸縮性,而不是確保容錯
(雖然我們后面會看到,容錯也實現了)。而且,游戲邏輯本身不知道、也不需要知道在
其他機器上運行著服務器的其他副本。游戲程序員編寫的代碼就像在一臺機器上執行一
樣,不同副本之間的協作由Darkstar項目的基礎設施來完成。實際上,如果游戲的容量
只需要一臺服務器,基于Darkstar的游戲就能夠在一臺服務器上運行。
客戶端連接到游戲邏輯使用的通信機制是基礎設施的一部分。這些機制支持客戶端到服
務器的直接通信,也支持一種“發布-訂閱”通道,任何發往通道的消息都會送達該通
道的所有訂閱者。
56 第3章
InfoQ企業軟件開發叢書《架構之美》精選版
42
圖3-1:Darkstar項目的高層架構
Darkstar棧由一組元服務來協調,這是一組網絡訪問服務,游戲或虛擬世界的程序員是
不可見的。這些元服務支持棧的各個副本之間進行協作,共同運營整個游戲。例如,這
些元服務將所有獨立的副本持續工作,如果某個副本失效,就會發起失效恢復。這些元
服務還會跟蹤各副本的負載,在需要的時候重新分配負載,或者隨時添加新的服務器,
增加總體容量。由于這些服務對于Darkstar項目的用戶來說是完全隱藏的,所以它們可
以隨時改變或移除,或者添加新的服務,這都不需要修改游戲或虛擬世界的代碼。
對于在Darkstar項目環境中創建游戲或虛擬世界的程序員來說,可見的架構就是棧中包
含的一組服務。服務的全集是可以改變和配置的,但4個基本服務必須存在,它們構成
了運營環境的核心,如圖3-2所示。
圖3-2:Darkstar棧
伸縮性架構設計57
客戶端客戶端
游戲
服務器
Darkstar
棧
Darkstar
元服務
Darkstar
元服務
Darkstar
棧
Darkstar
棧
Darkstar
棧
游戲
服務器
游戲
服務器
游戲
服務器
客戶端客戶端客戶端
其他服務
通道服務
客戶端會話服務
任務服務
數據服務
InfoQ企業軟件開發叢書《架構之美》精選版
43
3.3.2 基本服務
在這些棧層面的服務中,最基本的服務就是“數據服務”(Data Service),游戲或虛擬
世界用它來保存、讀取和操作所有持久數據。這里的持久概念可能比其他系統中的持久
概念更寬泛。對于在Darkstar項目環境中編寫的游戲或虛擬世界,任何存在時間超過一
個任務的數據都被視為持久的,必須在“數據服務”中保存。我們曾假定在這種編程模
型中任務的時間是短暫的(這也是需求),所以幾乎所有用于表示游戲或虛擬世界的服
務器端的數據都需要持久?!皵祿铡币矊⑦\行在不同服務器上的游戲或虛擬世界的
副本聯系在一起,因為所有這些副本都共享同一個(概念上的)“數據服務”實例。所
有的副本都會訪問相同的數據,所有的副本都可以根據需要讀取或改變存儲在“數據服
務”中的數據。
雖然“數據服務”看起來像是使用一個數據庫的好地方,但是存儲的需求實際上與通常
條件下對標準數據庫的需求有著很大的差別。存儲的對象之間靜態的關系很少,游戲中
也不需要對存儲的內容進行復雜的查詢。相反,簡單的命名策略就足夠了,包括在編程
語言層面上對對象的引用?!皵祿铡币脖仨氠槍ρ舆t進行優化,而不是針對吞吐量
來優化。特定服務要訪問的對象個數可能很少(我們初步的測算基于一些游戲和虛擬世
界的原型,這些測算表明每個任務大約訪問一打對象),在這些訪問的對象中,大約一
半會在任務執行中改變。
第二個棧層面的服務是“任務服務”(Task Service),它用于調度或執行任務。這些任
務要么是響應從客戶端收到的某個事件,要么是由游戲或虛擬世界服務器本身的內部邏
輯發起的。絕大部分任務是一次性事件,是由于客戶端的某種動作產生的,它們從“數
據服務”中讀取一些數據,操作這些數據,可能還進行一些通信,然后結束。任務也可
能生成其他的任務,或者生成定期任務,在特定的時間執行或以特定的時間間隔執行。
所有任務的執行時間必須很短,執行一項任務的最大時間是一個可配置的值,但默認值
是100毫秒。
游戲或虛擬世界的程序員會看到因事件或服務器邏輯本身而生成的單個任務,但在底層,
Darkstar的基礎設施正盡其所能調度最多的任務。特別地,由服務器邏輯生成的任務與響
應客戶發起的事件而生成的任務是并行執行的,就像響應不同客戶端而生成的任務一樣。
這樣的并發執行可能導致數據競爭。要處理這種競爭,就需要“任務服務”和“數據服
務”協作。在底層,在服務器程序員不可見的地方,“任務服務”調度的每個任務都包
裝在一個事務中。這個事務確保了任務中的所有操作要么全部完成,要么都不完成。此
外,所有改變“數據服務”中對象的值的操作都由該服務作為中介。如果有多個任務試
圖改變相同的數據對象,只有一個任務會執行,其他任務都會中止,并安排在稍后執行。
執行的那個任務會運行到結束。當執行的任務結束時,其他的任務就可以執行了。雖然
58 第3章
InfoQ企業軟件開發叢書《架構之美》精選版
44
服務器程序員可以說明訪問的數據將被修改,但這不是必需的。如果數據對象先被讀取,
然后被修改,“數據服務”會在任務提交之前檢測到這種修改。在讀取時就說明打算進
行修改,這是一種優化,能夠更早地檢測到沖突,但是不事先說明修改的意圖也不會影
響程序的正確性。
將任務包裝到一個事務中意味著通信機制也必須支持事務,只有當包裝了消息發送任務
的事務提交時,消息才會發出。這是通過Darkstar棧中余下兩項核心服務來完成的。
3.3.3 通信服務
第一個服務是“會話服務”(Session Service),它是客戶端和游戲或虛擬世界服務器之
間通信的中介。在登錄和認證后,客戶端與服務器之間就會建立起一個會話。服務器通
過會話監聽客戶端發出的消息,解析消息的內容,確定生成怎樣的任務來響應該消息。
客戶端通過會話接收來自服務器的響應。這些會話隱藏了客戶端和服務器的真實端點,
這一點對于Darkstar的多機伸縮性策略是很重要的。會話也負責確保維持消息的順序。
如果來自某個客戶端的前一條消息所引發的任務還沒有完成,后一條消息就不會提交。
在“會話服務”對任務進行這樣的排序之后,“任務服務”就得到了極大的簡化?!叭蝿?br /> 服務”可以假定它在任何時候收到的任務在本質上都是并發的。對來自特定客戶端的消
息排序是Darkstar框架中唯一的消息排序保證機制,外部觀察者看到的多個客戶端之間
的消息順序,與游戲或虛擬世界內看到的順序有很大不同。
Darkstar棧中總可以得到的第二種通信服務是“通道服務”(Channel Service)。通道是
一種一對多的通信機制。在概念上,通道可以由任何數目的客戶端加入,任何發往該通
道的消息都會送達所有與通道相關的客戶端。這里似乎是應用端到端技術的好地方,可
以讓客戶端之間直接通信,不會增加對服務器的負載。但是,這種通信需要由一些受信
任的代碼來監控,確保玩家不會利用不同的客戶端實現來發送不正確的消息或欺騙消息。
既然客戶端假定是在用戶或玩家的控制之下,那么客戶端的代碼就不能信任,因為很容
易把原來的客戶端代碼換成另外的“定制過的”客戶端代碼。所以,實際上,所有通道
消息都必須經過服務器,(可能)在經過服務器邏輯檢查之后。
會話和通道的復雜性有多種原因,其中之一就是它們必須遵守任務的事務語義。因此,
會話連接或通道上的實際消息傳送不能夠在調用相應的send()方法時發生,它只能夠
在該方法所處的任務提交時才能發生。
這些通信機制為我們實現伸縮性機制的第二部分奠定了基礎。既然所有通信都必須通過
Darkstar會話或通道的抽象層,而這些抽象層又不暴露客戶端或服務器通信的真實端點,
那么在實體通信和通信起止端的實際位置之間就存在著一個抽象層。這意味著我們可以
在Darkstar系統中將服務器通信的端點從一臺機器移到另一臺機器,同時不會改變客戶
伸縮性架構設計59
InfoQ企業軟件開發叢書《架構之美》精選版
45
端對這次通信的感覺。從游戲或虛擬世界的視角來看,通信也是經過一個會話或通道。
但是底層的基礎設施可以隨著時間的推移和負載的變化,根據負載平衡的需要,將會話
或通道從一臺機器移到另一臺機器。
3.3.4 任務的可移動性
要實現負載均衡的能力,其關鍵之處在于,對于我們要求的編程模型和必須使用的基本
棧服務,響應客戶端事件或游戲內部事件的任務可以從任何一臺運行著Darkstar棧和游
戲或虛擬世界副本的機器上移動到另一臺同樣的機器上。任務本身是用Java編寫的(注2),
這意味著只要(物理)機器的運行時棧中包含相同的Java虛擬機,任務就能夠運行。任
務讀取和操作的所有數據必須從“數據服務”獲得,“數據服務”是所有機器上的游戲
或虛擬世界的實例和Darkstar棧所共享的。通信由“會話服務”或“通道”來實現中介,
它們抽象了通信的真實端點,而且支持特定的會話和通道從一個服務器移動到另一個服
務器上。因此,所有任務都可以運行在任意一個游戲服務器的實例上,同時不改變任務
的語義。
這使得Darkstar的基本伸縮機制看起來很簡單。如果有一臺機器超載了,只要將一些任
務從這臺機器移到另一臺負載較小的機器就行了。如果所有的機器都超載了,就向運行
著Darkstar棧和游戲/虛擬世界的集群中添加新的機器。底層的負載平衡軟件會將負載分
發給新的機器。
對單臺機器的負載進行監控并在需要時重新分配負載,這是元服務的工作。這些元服務
是網絡層面的服務,對于游戲或虛擬世界的程序員是不可見的,但是它們對Darkstar棧
中的服務是相互可見的。例如,這些元服務會監控哪些機器正在運行(并監控是否有機
器失效),哪些用戶與某臺機器有關,不同的機器當前的負載。由于元服務對于游戲或
虛擬世界的程序員是不可見的,所以它們在任何時候都可以改變,不會影響到游戲邏輯
的正確性。這讓我們能夠嘗試不同的策略和方法,實現系統的動態負載平衡,也讓我們
能夠豐富基礎設施所需的元服務集合。
同樣,我們使用實現多機伸縮機制來實現系統的高容錯。由于任務和通信機制所使用的
數據是與具體機器無關的,所以很明顯,我們可以將任務從一臺機器移到另一臺機器上。
但是如果機器失效,我們如何恢復在那臺機器上執行的任務呢?答案很簡單:任務本身
也是持久對象,保存在系統的“數據服務”中。因此,如果一臺機器失效,該機器上正
在執行的所有任務都被視為中斷的事務,會重新調度在不同的機器上執行。盡管這種重
60 第3章
注2: 更準確地說,所有的任務都由字節碼的序列組成,可以在Java虛擬機上執行。我們不關
心源語言是什么,我們只關心從源語言編譯出來的結果可以運行在所有支持游戲或虛擬
世界的分布式環境中。
InfoQ企業軟件開發叢書《架構之美》精選版
46
新調度比在一臺機器上重新調度中斷的任務的延遲要長,但系統的正確性是不變的。系
統的用戶(游戲玩家或虛擬世界的居民)頂多會注意到響應時間暫時有點延長。這樣的
延遲讓人有點不舒服,但總好過現在其他游戲或虛擬世界環境中服務器崩潰所造成的影
響。在那些環境中,會導致玩家掉線,還可能導致相當一部分游戲狀態的丟失。
3.4 關于架構的思考
也許所有人關于架構及其實現的第一個問題就是它的性能。雖然未經深思熟慮就對架構
進行優化是諸多罪惡之源,但是我們也可能設計出一個架構,而它的實現根本不可能達
到好的性能。由于Darkstar架構的一項基本選擇,這種擔心是真實的。而且由于游戲行
業的特點,確定服務器基礎設施的性能是很難的。
要確定游戲或虛擬世界服務器基礎設施的性能,其難度源自一個簡單的事實:沒有針對
大規模MMO或虛擬世界的性能測試標準或共同接受的例子。缺少性能測試標準并不奇
怪,因為絕大多數游戲或虛擬世界的服務器組件都是針對特定的游戲或虛擬世界從頭開
發的。只有少數的通用基礎設施可以作為可復用的構建塊,這些組件一般是事后從特定
的游戲或虛擬世界中提取出來的,提供給其他構建類似游戲的人使用。也許是因為游戲
行業還比較年輕,或是因為娛樂業中出現重要技術的偶然性,結果是沒有共同接受的性
能測試標準用于測試新的基礎設施,也無法對不同的基礎設施進行比較。
關于游戲或虛擬世界的預期計算、數據操作和通信負載也基本上沒有什么資料,所以很
難創建性能測試標準程序。部分原因是已有的服務器都是定制的。每臺服務器都是為特
定的游戲或虛擬世界設計的,所以考慮的是那款游戲或虛擬世界的具體負載特征。而且,
這種狀況也是因為游戲業的強烈的保密性。在游戲業中,關于一款游戲開發的信息是嚴
格保密的,而且發布游戲的實現方式也是嚴格保密的。同時,行業中的許多人對這些信
息是不感興趣的。大量的思考和討論集中在藝術設計、故事情節或玩家交互模式上,這
些令新游戲更有趣、更好玩,而不是游戲服務器的設計方式和游戲為支持并發玩家(這
個數字也是嚴格保密的)所采取的伸縮性技術。所以,獲得現有的游戲和虛擬世界的這
種服務器負載信息都很困難。
根據我們的經驗,即使我們能夠找來開發者,談論他們的游戲或虛擬世界加在服務器上
的負載,他們也常常會報告錯誤的信息。這不是因為他們想保持商業優勢而故意錯誤報
告他們服務器的情況,而是因為他們真的自己不也了解。在游戲服務器上基本不會加入
一些手段,讓他們收集有關服務器真實性能或完成事務的信息。對于這種服務器的分析
一般最多是經驗性的。程序員在服務器上工作,直到它讓游戲玩起來有趣,這是一種迭
代式的工作方式,而不是仔細對代碼本身進行測量。在這些系統中,更多的是手工技術
活,而不是科學測定。
伸縮性架構設計61
InfoQ企業軟件開發叢書《架構之美》精選版
47
這并不是說,這些游戲或虛擬世界后面的服務器是一些粗制濫造的代碼,也不是說它們
做得不好。實際上,許多代碼是效率杰作,展示了聰明的編程技巧,也展示了針對高要
求的應用構造一次性、專門目的服務器的優勢。但是,為每個游戲或虛擬世界構建一個
新服務器的習慣意味著人們不太注意積累構建這種服務器所需的知識,也沒有共同接受
的機制來比較不同的基礎設施。
3.4.1 并行與延遲
缺少有關服務器可接受的性能的資料,這一點引起了Darkstar團隊的特別關注,因為我
們所做的一些關鍵決定,都是圍繞著如何能夠從游戲或虛擬世界服務器中獲得好的性能。
也許Darkstar架構和一般實踐之間的最大區別就在于,Darkstar架構拒絕在服務器的主內
存中存放任何重要的信息。所有生存周期超過一次具體任務的數據都需要持久在“數據
服務”中,這是實現Darkstar基礎設施功能的核心。這讓基礎設施能夠檢測到并發問題,
反過來又讓系統能夠對程序員隱藏這些問題,同時讓服務器能夠利用多核架構。它也是
實現整體伸縮性的關鍵組件,支持任務從一臺服務器移動到另一臺服務器,從而在一組
機器上實現負載平衡。
在游戲和虛擬世界服務器領域,任何時候都持久保存游戲狀態是一種異端邪說,因為人
們普遍很關心延遲。在編寫這種服務器時,大家的觀點是只有將所有信息都放在內存中
才能讓延遲足夠小,達到要求的響應時間。可以偶爾保存狀態的快照,但對交互速度的
要求表明,這種長時間的操作只能夠偶爾進行,而且要在后臺進行。所以,從表面上看,
我們的架構似乎絕不可能達到足夠好的性能,從而服務于它的目標應用。
雖然要求數據持久肯定是這個架構的主要不同之處,而且要求通過“數據服務”來訪問
數據會在架構中引入一定的延遲,但我們相信我們所采取的方式更具有競爭力,原因有
幾點。首先,我們相信能夠讓訪問內存數據和訪問“數據服務”中的數據之間的差異遠
遠小于一般人們的看法。雖然在概念上每個生命周期超出一次任務的對象都需要從持久
存儲中讀出,并寫入持久存儲,但實現這種存儲可以利用人們在數據庫緩存和一致性方
面多年的研究成果,從而減少因這種方式而導致的數據訪問延遲。
如果我們能夠將訪問局限在一個特定服務器上的幾組特定對象,就更是如此了。如果用
到一組特定對象的那些任務都運行在一臺服務器上,那么就可以利用該服務器的緩存,
達到接近內存的對象讀寫速度(受到需要滿足的持久性約束的影響)。我們可以識別任
務屬于哪些玩家或虛擬世界的哪些用戶。這樣,我們就可以利用基礎設施中服務所接收
到的數據訪問和通信請求,來收集特定時刻游戲或虛擬世界中數據訪問模式和通信模式
的信息。有了這些信息,我們相信能夠非常準確地估計哪些玩家應該與另一些玩家放在
一起。因為我們可以根據需要將玩家移動到任何服務器上,所以能夠根據觀察到的運行
62 第3章
InfoQ企業軟件開發叢書《架構之美》精選版
48
時行為,主動地將相關的玩家盡可能地放在同一臺服務器上。這讓我們能夠運用數據庫
領域熟悉的標準緩存技術,盡量減少訪問和保存持久信息的延遲。
這聽起來非常像目前在大規模游戲和虛擬世界中為實現伸縮性而采用的地理區域分解技
術。在這種技術中,服務器開發者將世界分解成一些區域,將它們指派給一些服務器,
不同的區域就成為用戶分區的機制。同一區域的玩家比不同區域的玩家進行交互的可能
性更大,所以這種集中在一個服務器上的優勢就體現出來了。不同之處在于,目前的地
理區域分解是在游戲開發過程中進行的,被編入源代碼,放到服務器上。而我們的位置
集中基于運行時的信息,可以根據游戲中發生的實際玩法和交互模式來實現動態調整。
這類似于編譯時優化和運行時優化之間的區別。前一種方法試圖針對程序所有可能的運
行進行優化,而后一種方法試圖針對當前的運行進行優化。
我們不相信我們能夠消除內存訪問和持久訪問之間的差別,而且我們也不認為有必要這
樣做,最后讓這種架構比使用內存的架構性能更好。要知道,通過讓所有的數據持久,
我們可以支持在服務器上使用多線程(從而也支持多核)。盡管我們不相信并發是完美
的(即對于每個添加的核,我們都能充分利用),但我們確實相信在游戲和虛擬世界中
可以使用大量的并行運算(初步的結果也證實了這種看法)。如果可使用的并行運算超
過我們可能引入的延遲,那么游戲或虛擬世界的總體性能就會更好。
3.4.2 賭未來
我們對多核處理器中多線程的信心基本上是在賭處理器將來的發展方向。目前服務器的
處理器提供2~32個核,我們相信將來的芯片設計將集中向更多的核發展,而不是讓現
有的核以更高的時鐘頻率運行。當我們在幾年前開始這個項目時,這種賭博似乎比現在
更具投機性。那時候,我們在做展示時常常說這種設計是一種“假定”的演練,說我們
正嘗試一種架構,如果芯片性能更好地支持多線程而不是單線程的時鐘速度,這種架構
將是可行的。這就是在研究實驗室中進行這類項目的好處之一,可以接受設計方法中存
在很高的風險,探索一個將來也許在商業上可行的領域。我們決定構建一個以多線程為
中心的架構,與這個決定做出時相比,目前芯片設計的趨勢讓我們看得更清楚。(注3)
即使我們只能得到50%的完美并發,如果我們能把使用持久存儲的延遲控制在使用內存
的延遲的2~16倍,就能夠在性能上持平。我們相信在并發方面以及減少訪問持久狀態
和全內存方案之間的差異方面都可以做得更好。但是結果主要取決于構建于這個基礎設
施之上的應用的使用模式(我們曾提到,這一點很難發現)。
我們也不應認為減少延遲就是這個基礎設施的唯一目標。通過將游戲或虛擬世界服務器
端的對象全部保存在“數據服務”中,我們把因服務器失效而導致的數據丟失減到了最
伸縮性架構設計63
注3: 再一次說明,在早期的設計決定中少許的運氣是很重要的。
InfoQ企業軟件開發叢書《架構之美》精選版
49
小。實際上,在大多數情況下,服務器失效時用戶只會注意到延時有一點增加,因為任
務(它們本身也是持久對象)從失效的服務器移到了另一臺服務器上。沒有數據會丟失。
一些緩存機制可能導致丟失幾秒鐘的游戲成果,但即使是這樣,也比在線游戲和虛擬世
界目前使用的機制好得多,它們只是將偶爾進行內存快照作為主要的持久方式。在它們
的基礎設施中,如果服務器在不巧的時間崩潰,可能會造成數小時的游戲成果丟失。只
要延遲是可以接受的,Darkstar所使用持久機制的可靠性更高,這對于在這個基礎設施
上構建系統的開發者和系統的用戶來說都是優點。
3.4.3 簡化程序員的工作
實際上,如果在支持伸縮性的同時減少延遲是服務器開發者的唯一目標,那么開發者最
好的方法就是專門針對特定的游戲,編寫自己的分布式和多線程基礎設施。但這要求服
務器開發者處理復雜的分布式和并發編程。在為速度需求而過度煩惱之前,我們應該想
到Darkstar的第二個同樣重要的目標,即在支持多線程、分布式游戲產品的同時,為程
序員提供一個單機單線程的開發模型。
在相當大的程度上,我們已經實現了這一目標。通過將所有任務封裝到事務中,并在
“數據服務”中檢測數據沖突,程序員就能夠享受到多線程的好處,又不必在他們的代碼
中引入鎖協議、同步和信號量。程序員不必擔心如何將玩家從一臺服務器移到另一臺服
務器,因為Darkstar為他們提供了透明的負載平衡。編程模型雖然有自己的風格和限制,
但社區中的早期成員認為,這對他們開發的那種游戲和虛擬世界來說是比較自然的。
不幸的是,我們發現我們不能夠向程序員隱藏所有的東西。當在Darkstar上編寫的第一
個游戲表現出極少的并行度(以及意料之外的糟糕性能)時,這一點就明確了。通過檢
查源代碼,我們很快就發現了原因。游戲中的數據結構設計導致了游戲中所有的狀態改
變都只涉及一個對象,并由它來協調所有的工作。使用這個對象實際上使得游戲中所有
動作序列化執行,這使得基礎設施不能夠發現并利用并發計算。
當我們發現這一點時,我們與游戲開發者進行了長時間的討論,主題是在設計對象時需
要考慮到并發訪問。通過對游戲中數據對象進行審查,我們發現了一些類似的情況:數
據設計方案排除了并發的可能性(并非出自設計者本意)。當這些對象重新設計之后,
系統的整體性能增加了好幾個數量級。
這告訴我們,不可能讓使用Darkstar的開發者完全不知道系統底層的并發和分布式實質。
但是,他們對系統這方面特點的知識不需要包括并發控制、鎖,以及在系統的各個分布
式部分之間的通信。實際上,他們只需要在設計活動中確保他們的數據對象定義能夠充
分利用并發。這種設計一般只需要確保對象定義是自包含的,它們的操作不需要依賴其
他對象的屬性。這一點對于任何系統來說,都不是不好的設計原則。
64 第3章
InfoQ企業軟件開發叢書《架構之美》精選版
50
關于Darkstar架構,我們還有許多方面沒有測試,或者說我們并未完全理解。雖然我們
已經得到了一個系統,使得多臺機器能夠利用多線程運行一個游戲或虛擬世界,同時對
服務器程序員(幾乎)保持透明,但是我們還沒有通過添加核心服務之外的其他服務,
來檢驗該架構的能力。由于Darkstar任務的事務本質,這可能比我們開始設想的要復雜
得多,但我們希望這些添加的服務不需要參與到核心服務的事務中。我們已經開始試驗
通過不同的方式來收集系統負載的信息和實現負載平衡。幸運的是,因為實現這種負載
平衡的機制對于使用系統的程序員是完全不可見的,所以我們可以移除老的方式,引入
新的方式,同時又不影響Darkstar的用戶。
作為一個架構,Darkstar展示了一些創新的方法,這使它變得很有趣。它試圖構造一個
游戲或虛擬世界的基礎設施,使其具有企業級軟件一樣的可靠性,同時又滿足游戲行業
對延遲、通信和伸縮性的要求。它是目前為數不多的這類嘗試之一。通過利用更多機器
和更多線程來實現效率,我們希望能夠抵消因使用持久存儲機制而導致的延遲增加。最
后,游戲和虛擬世界環境中極為不同的情況,即客戶端的處理很多而服務器端的處理很
少,與我們常見的高并發、分布式系統環境形成了鮮明的對比?,F在說這個架構是否成
功還為時尚早,但我們相信它已經很有趣了。
伸縮性架構設計65
InfoQ企業軟件開發叢書《架構之美》精選版
51
?
?
?
?
?
?
?
?
?
?
?
?
直接的HTML標簽
如果FBML服務返回標簽<p/>,Facebook將在輸出頁上直接渲染為<p/>。作為Web展現
的基石,大多數HTML標簽都是支持的,少數違反Facebook層面的信任或設計期望的標
簽除外。
所以FBML字串<h2>Hello, welcome to <i>Fetterman’s books!</i></h2>在渲
染成HTML時,實際上是保持不變的。
數據顯示標簽
這里是體現數據威力的地方。假定個人簡介照片不能轉到其他站點。通過指定
<fb:profile-pic uid="8055">,開發者就可以在他們的應用中顯示更多的Facebook
用戶信息,同時不要求用戶完全信任開發者,將這部分信息交給開發者處理。
例如:
<fb:profile-pic uid="8055" linked="true" />
翻譯成FBML:
<a href="http://www.facebook.com/profile.php?id=8055"
οnclick="(new Image()).src = '/ajax/ct.php?app_id=...">
<img uid="8055" linked="true" src="..." alt="Dave Fetterman" title="Dave Fetterman" />
</a>
注意:復雜的onclick屬性在生成時會在Facebook頁面顯示中限制Javascript。
請注意,即使信息受到了保護,這些內容也不會返回到應用棧中,只是顯示給用戶看。
在容器端執行使這些數據可以查看,但不要求將它們交給應用程序!
數據執行標簽
作為使用隱藏數據的一個更好的例子,用戶的隱私限制只能通過內部的can_see方法來
訪問,它是應用體驗的一個重要部分,但不能通過數據服務從外面進行訪問。利用
<fb:-if-can-see>標簽和其他類似的標簽,應用可以通過屬性來指定一個目標用戶,
這樣只有當查看者能夠看到目標用戶的特定內容時,那些子元素才會渲染出來。因此,
隱私數據本身不會暴露給應用,同時應用又能滿足強制實現的隱私設置。
從這個角度來說,FBML是一個受信任的聲明式執行環境,與C或PHP這樣的必須服從的
執行環境不同。嚴格來說,FBML不像這些語言那樣是“圖靈完備”的(例如,沒有提供
循環結構)。像HTML一樣,除了樹狀遍歷所隱含的狀態外,在執行時不保存任何狀態;
例如,<fb:tab-item>只在<fb:tabs>之內有意義。但是,通過讓受信任系統中的用戶獲
得數據,FBML提供了大量功能,這些功能正是大多數開發者希望提供給他們的用戶的。
134 第6章
InfoQ企業軟件開發叢書《架構之美》精選版
77
Facebook數據(朋友、主要網絡)、CSS樣式和針對鍵盤動作的JavaScript。像這樣的標
簽讓容器站點可以推廣某些設計模式和應用間的公用元素,也讓開發者能夠快速實現他
們想要的功能。
FBML:一個小例子
請回憶一下我們在創建假想的外部網站時,通過引入friends.get和users.getInfo
API對原來的http://fettermansbooks.com代碼實現改進。接下來我們將展示一個例子,看
看FBML如何能夠結合社會關系、私有業務邏輯和完全集成的應用的感覺。如果我們能
夠通過數據庫調用book_get_all_reviews($isbn)獲得一本書的全部書評,那么我
們就可以將朋友數據、私有業務邏輯和“墻式”風格結合起來,利用FBML在容器站點
上顯示書評,代碼如例6-23所示。
例6-23:利用FBML創建一個應用
// Wall-style social book reviews on Facebook
// FBML Tags used: <fb:profile-pic>, <fb:name>, <fb:if-can-see>,
<fb:wall>
// from section 1.3
$facebook_friend_uids = $facebook_client->api_client->friends_get();
foreach($facebook_friend_uids as $facebook_friend) {
if ($books_user_id = books_user_id_from_facebook_id($facebook_friend))
$book_site_friends[] = $books_user_id;
}
// a hypothesized mapping, returning
// books_uid -> book_review object
$all_reviewers = get_all_book_reviews($isbn);
$friend_reviewers = array_intersect($book_site_friends, array_keys($all_reviewers));
echo 'Friends' reviews:<br/>';
echo '<fb:wall>';
// put friends up top.
foreach ($friend_reviewers as $book_uid => $review) {
echo '<fb:wallpost uid="'.$book_uid.'">';
echo '(' . $review['score'] . ')' . $review['commentary'];
echo '</fb:wallpost>';
unset($all_reviewers[$book_uid]); // don't include in nonfriends below.
}
echo 'Other reviews:<br/>';
// only nonfriends remain.
foreach ($all_reviewers as $book_uid => $review) {
echo '<fb:if-can-see uid="'.$book_uid.'">'; // defaults to 'search' visibility
echo '<fb:wallpost uid="'.$book_uid.'">';
echo '(' . $review['score'] . ')' . $review['commentary'];
136 第6章
InfoQ企業軟件開發叢書《架構之美》精選版
79
echo '</fb:wallpost>';
echo '</fb:if-can-see>';
}
echo '</fb:wall>';
雖然這采用的是輸出FBML的服務的形式,而不是輸出HTML的Web調用,但一般流程
是不變的。這里,Facebook數據讓應用能夠在無關的書評之前,顯示更多的相關書評
(朋友的書評),并且使用了FBML來顯示結果,采用了Facebook上相應的隱私邏輯和設
計元素。
6.4.4 FBML架構
將開發者提供的FBML翻譯成顯示在http://facebook.com上的HTML,需要一些技術和概
念綜合作用:將輸入字符串解析成一棵句法樹,將這棵樹中的標簽轉換成內部方法調用,
應用FBML語法規則,保持容器站點的約束。像FQL一樣,這里我們將關注點主要放在
FBML與平臺數據的交互上,對其他的技術則不作詳細探討。FBML處理了一個復雜的
問題,FBML的全部實現細節是相當多的—我們省略的內容包括FBML的錯誤日志、
為后來的渲染事先緩存內容的能力、表單提交結果的安全性簽名等。
首先,看看解析FBML的低層問題。在繼承了瀏覽器的某些角色的同時,Facebook也繼
承了它的一些問題。為了方便開發者,我們不要求提供的輸入可以通過schema驗證,甚
至不要求是結構良好的XML—不封閉的HTML標簽,如<p>(與XHTML不同,即
<p/>)打破了輸入必須作為真正的XML進行解析的假定。因為這一點,我們需要一種
方法將輸入的FBML字符串先轉換成結構良好的句法樹,包含標簽、屬性和內容。
為了做到這一點,我們采用了采用了一個開放源代碼瀏覽器的一些代碼。本章將這部分
處理視為一個黑盒,所以我們現在假定,在接收到FBML并經過這樣的處理流程后,我
們得到了名為FBMLNode的樹狀結構,它讓我們能夠查詢生成的句法樹中任何節點的標
簽、屬性鍵值對和原始內容,并能夠遞歸查詢子元素。
從最高的層面上看,我們可以注意到FBML出現在Facebook站點的所有地方:應用“畫
布”頁面、新聞信號源的故事內容、個人簡介框的內容等。每種上下文中或每種“風味”
的FBML都定義了對輸入的約束,例如,畫布允許使用iframe,而個人簡介框則不允許。
很自然,因為FBML維護數據隱私的方式與API類似,所以執行上下文中必須包含查看
用戶的ID和生成該內容的應用ID。
所以,在我們真正開始有效使用FBML之前,先要看看環境的規則,它由FBMLFlavor
類來封裝,如例6-24所示。
數據增長:Facebook平臺的架構137
InfoQ企業軟件開發叢書《架構之美》精選版
80
例6-24:FBMLFlavor類
abstract class FBMLFlavor {
// constructor takes array containing user and application_id
public function FBMLFlavor ($environment_array) { ... }
public function check($category) {
$method_name = 'allows_' . $category;
if (method_exists($this,$method_name)) {
$category_allowed = $this->$method_name();
} else {
$category_allowed = $this->_default();
}
if (!$category_allowed))
throw new FBMLException('Forbidden tag category '.$category.' in
this flavor.');
}
protected abstract function _default();
}
下面是這個抽象類的一個子類,它對應于渲染FBML的頁面或元素。例6-25是一個例子。
例6-25:FBMLFlavor類的一個子類
class ProfileBoxFBMLFlavor extends FBMLFlavor {
protected function _default() { return true; }
public function allows_redirect() { return false; }
public function allows_iframes() { return false; }
public allows_visible_to() { return $this->_default(); }
// ...
}
這種風味類的設計很簡單:它包含了隱私上下文(用戶和應用),實現了檢查方法,為
稍后將展示的FBMLImplementation類中包含的豐富邏輯建立了規則。與平臺API的實
現層很像,這個實現類為服務提供了實際的邏輯的數據訪問,其他的代碼為這些方法提
供了訪問入口。每個Facebook特有的標簽,如<fb:TAG-NAME>,將有一個對應的實現
方法fb_TAG_NAME(例如,類方法fb_profile_pic將實現<fb:profile-pic>標簽
的邏輯)。每個標準的HTML標簽也都有一個對應的處理方法,名為tag_TAG_NAME。這
些HTML處理方法通常讓數據無變化地通過,但是即便是對一些“普通”的HTML元素,
FBML常常也需要進行檢查和轉換。
讓我們來看看某些標簽的實現,然后將它們結合起來討論。每個實現方法都接收一個來
自FBML解析器的FBMLNode,以字符串的方式返回輸出的HTML。下面是一些直接的
HTML標簽、數據顯示標簽和數據執行標簽的實現示例。請注意,這些程序清單用到了
一些功能,在這里沒有完整而詳細地列出。
在FBML中實現直接的HTML標簽
例6-26包含了<img>標簽的內部FBML實現。圖像標簽的實現包含更多的邏輯,有時候
138 第6章
InfoQ企業軟件開發叢書《架構之美》精選版
81
需要將圖像源的URL重寫到Facebook服務器上圖像緩存的URL。這體現了FBML的強大:
應用??梢苑祷嘏cHTML非常相似的標記語言,支持它自己的站點,而Facebook可以通
過純技術的手段強制實現平臺所要求的行為。
例6-26:fb:img標簽的實現
class FBMLImplementation {
public function __construct($flavor) {... }
// <img>: example of direct HTML tag (section 4.3.1)
public function tag_img($node) {
// images are not allowed in some FBML contexts -
// for example, the titles of feed stories
$this->_flavor->check('images');
// strip of transform attribute key-value pairs according to
// rules in FBML
$safe_attrs = $this->_html_rewriter->node_get_safe_attrs($node);
if (isset($safe_attrs['src'])) {
// may here rewrite image source to one on a Facebook CDN
$safe_attrs['src'] = $this->safe_image_url($safe_attrs['src']);
}
return $this->_html_rewriter->render_html_singleton_tag($node->
get_tag_name(), $safe_attrs);
}
}
在FBML中實現數據顯示標簽
例6-27展示了通過FBML使用Facebook數據的例子。<fb:profile-pic>用到了uid、size
和title屬性,將它們結合起來,根據內部數據產生HTML輸出,并符合Facebook的標準。
在這個例子中,輸出是指定用戶名的個人簡單照片,鏈接到用戶的個人簡介頁面,只在當
查看者能看到這部分內容時才顯示。這個功能也存在于FBMLImplementation類中。
例6-27:fb:profile-pic標簽的實現
// <fb:profile-pic>: example of data-display tag
public function fb_profile_pic($node) {
// profile-pic is certainly disallowed if images are disallowed
$this->check('images');
$viewing_user = $this->get_env('user');
$uid = $node->attr_int('uid', 0, true);
if (!is_user_id($uid))
throw new FBMLRenderException('Invalid uid for fb:profile_pic ('.$uid .')');
$size = $node->attr('size', "thumb");
$size = $this->validate_image_size($size);
if (can_see($viewing_user, $uid, 'user', 'pic')) {
數據增長:Facebook平臺的架構139
InfoQ企業軟件開發叢書《架構之美》精選版
82
// this wraps user_get_info, which consumes the user's 'pic' data field
$img_src = get_profile_image_src($uid, $size);
} else {
return '';
}
$attrs['src'] = $img_src;
if (!isset($attrs['title'])) {
// we can include the user name information here too.
// again, this function would wrap internal user_get_info
$attrs['title'] = id_get_name($id);
}
return $this->_html_renderer->render_html_singleton_tag('img', $attrs);
}
FBML中的數據執行標簽
FBML解析的遞歸本質使得<fb:if-can-see>標簽就像是標準的必須服從的控制流中
的if語句一樣,它是FBML實際控制執行的一個例子。這是FBML實現類中的另一個方法,
例6-28列出了它的細節。
例6-28:fb:if-can-see標簽的實現
// <fb:if-can-see>: example of data-execution tag
public function fb_if_can_see($node) {
global $legal_what_values; // the legal attr values (profile, friends, wall, etc.)
$uid = $node->attr_int('uid', 0, true);
$what = $node->attr_raw('what', 'search'); // default is 'search' visibility
if (!isset($legal_what_values[$what]))
return ''; // unknown value? not visible
$viewer = $this->get_env('user');
$predicate = can_see($viewer, $uid, 'user', $what);
return $this->render_if($node, $predicate); // handles the else case
for us
}
// helper for the fb_if family of functions
protected function render_if($node, $predicate) {
if ($predicate) {
return $this->render_children($node);
} else {
return $this->render_else($node);
}
}
protected function render_else($node) {
$html = '';
foreach ($node->get_children() as $child) {
if ($child->get_tag_name() == 'fb:else') {
$html .= $child->render_children($this);
}
}
140 第6章
InfoQ企業軟件開發叢書《架構之美》精選版
83
return $html;
}
public function fb_else($ignored_node) { return ''; }
如果某對“觀察者-目標”通過了can-see檢查,引擎就會遞歸地渲染<fb:if-can-see>
節點的子節點。否則,就會渲染可選標簽<fb:else>子節點下的內容。請注意
fb_if_can_see直接訪問<fb:else>子節點的方式;如果<fb:else>出現在這樣的一個
“if風格”的FBML標簽之外,標簽和它的子標簽就不會返回任何內容。所以,FBML不僅
僅是一個簡單的轉換式例程,它會注意到文檔的結構,因此可以包含條件控制流的元素。
結合在一起
前面討論的每個功能,都需要注冊為一個回調,在解析輸入的FBML時使用。在
Facebook(以及它的開放源代碼平臺實現中),這個“黑盒”解析器是用C寫的PHP擴展,
每個回調都存在于PHP樹中。要完成這種高層控制流,我們必須向FBML解析引擎聲明
這些標簽。和其他地方一樣,出于簡單性考慮,例6-29也是經過了大量編輯的。
例6-29:FBML主要求值流程
// As input to this flow:
// $fbml_impl - the implementation instantiated above
// $fbml_from_callback - the raw FBML string created by the external
application
// a list of "Direct HTML" tags
$html_special = $fbml_impl->get_special_html_tags();
// a list of FBML-specific tags (<fb:FOO>)
$fbml_tags = $fbml_impl->get_all_fb_tag_names();
// attributes of all tags to rewrite specially
$rewrite_attrs = array('onfocus', 'onclick', /* ... */);
// this defines the tag groups passed to flavor's check() function
// (e.g. 'images', 'bold', 'flash', 'forms', etc.)
$fbml_schema = schema_get_schema();
// Send the constraints and callback method names along
// to the internal C FBML parser.
fbml_complex_expand_tag_list_11($fbml_tags, $fbml_attrs,
$html_special,$rewrite_attrs, $fbml_schema);
$parse_tree = fbml_parse_opaque_11($fbml_from_callback);
$fbml_tree = new FBMLNode($parse_tree['root']);
$html = $fbml_tree->render_html($fbml_impl);
FBML利用回調擴展了瀏覽器的解析技術,包裝了由Facebook創建和管理的數據、執行和
展現宏。這個簡單的思想實現了應用的完全集成,支持使用通過API暴露出來的內部數據,
數據增長:Facebook平臺的架構141
InfoQ企業軟件開發叢書《架構之美》精選版
84
同時保持安全性方面的用戶體驗。FBML本身幾乎就是一種編程語言,它也是充分發展后
的數據:外部提供的聲明式執行,安全地控制了Facebook上的數據、執行和顯示。
6.5 系統的支持功能
現在,開發者創建的軟件運行在Facebook的服務之上,不僅是結合了界面組件,而是全
部的應用。在這個過程中,我們創造了一個社會關系網絡應用的完全不同的概念。我們
從一個典型的Web應用的獨立數據、邏輯和顯示的標準設置開始,不考慮所有社會關系
數據,只是讓用戶可以確信能夠作出貢獻。現在,我們取得了充分的進展,應用使用了
Facebook的社會關系數據服務,同時它自己又成為一個FBML服務,完全集成到容器站
點之中。
Facebook數據也獲得了長足的發展,不再僅僅是本章第一節討論的內部庫。但是,仍有
一些重要的、常見的Web使用場景和技術,目前平臺還未能支持。通過將應用變成一個
返回FBML的服務,而不是直接由瀏覽器解讀的HTML/CSS/JS,我們接觸到了關于現代
Web應用的一些重要假定。讓我們來看看Facebook平臺如何修正這樣一些問題。
6.5.1 平臺cookie
應用的新Web架構排除了瀏覽器內建的一些技術,許多Web應用棧可能依賴于這些技術。
可能其中最重要的一點是,過去瀏覽器用于保存用戶與應用棧交互信息的cookie不再可
以得到了,因為應用的目標消費者不再是瀏覽器,而是Facebook平臺。
初看上去,伴隨對應用棧的請求發送一些cookie似乎是一個不錯的解決方案。但是,這
些cookie的作用域現在是“http://facebook.com”,而實際上,cookie信息屬于該應用領域
所提供的用戶體驗。
解決方案是什么?讓Facebook具有瀏覽器的職責,在Facebook自己的存儲庫中復制這種
cookie功能。如果應用的FBML服務送回請求頭,試圖設置瀏覽器cookie,Facebook就保
存這個cookie信息,以(user, application_id)對為主鍵。Facebook然后“重新創
建”這些cookie,就像用戶向這個應用棧發出后續請求時瀏覽器所做的一樣。
這個解決方案很簡單,在開發者從HTML棧方式轉向FBML服務方式轉變時,只需要很
少的改變。請注意,當用戶決定在這個應用提供的HTML棧上導航時,這種信息是不能
使用的。另一方面,它可以有效地分離用戶在Facebook上的應用體驗和在應用的HTML
站點上的應用體驗。
6.5.2 FBJS
當應用棧作為一個FBML服務被使用,而不是直接由用戶的瀏覽器來使用,Facebook就
142 第6章
InfoQ企業軟件開發叢書《架構之美》精選版
85
沒有機會執行瀏覽器端的腳本。直接返回未修改過的開發者提供的內容(一個不充分的
解決方案,這在FBML小節的一開始就討論過)可以解決這個問題,但它違反了
Facebook在顯示體驗上所加的約束。例如,當加載用戶的簡介頁面時,Facebook不希望
在加載事件上觸發一個彈出窗口。但是,限制所有的JavaScript會排除許多有用的功能,
如AJAX或在不重新加載的情況下動態操作頁面的內容。
相反,FBML在解釋開發者提供的<script>樹和其他頁面元素的內容時會考慮到這些
約束。在此之上,Facebook提供了一些JavaScript庫,讓這些場景容易實現,同時又受
到控制。這些修改共同構成了Facebook的平臺JavaScript仿真套件,稱為FBJS,它通過
以下幾點,讓應用既動態又安全:
? 重寫FBML屬性,確保實現虛擬文檔范圍。
? 延遲激活腳本內容,直到用戶在頁面或元素上發起動作時。
? 提供一些Facebook庫,以受控的方式來實現常見的腳本使用場景。
很清楚,不是所有的實現自有平臺的容器站點都需要這些修改,但FBJS向我們展示了幾
種解決方案,這樣的新Web架構需要這些解決方案來繞過一些困難。我們在這里只展示
了這些解決方案的一般思想,FBJS的許多部分還需要不斷改進,與FBML和可擴展的專
有JavaScript庫進行融合。
首先,JavaScript通??梢栽L問包含它的文檔的整個文檔對象模型(DOM)樹。但是在
平臺畫布頁面中,Facebook包含了許多它自己的元素,開發者不允許對它們進行修改。
解決方案是什么?在用戶提供的HTML元素和JavaScipt符號之前加上前綴,即應用的ID
(如app1234567)。通過這種方式,在開發者的JavaScript中如果試圖調用不允許調用的
alert()函數,就會調用未定義的函數app1234567_alert,并且只有開發者自己提供
的那部分文檔的HTML可以被document.getElementById這樣的JavaScript代碼訪問。
作為FBJS需要對提供的FBML(包括<script>元素)進行這種轉換的一個例子,我們
創建了一個簡單的FBML頁面,實現了AJAX功能,如例6-30所示。
例6-30:一個使用FBJS的FBML頁面
These links demonstrate the Ajax object:
<br /><a href="#" οnclick="do_ajax(Ajax.RAW); return false;">AJAX
Time!</a><br />
<div>
<span id="ajax1"></span>
</div>
<script>
function do_ajax(type) {
var ajax = new Ajax(); // FBJS Ajax library.
ajax.responseType = type;
數據增長:Facebook平臺的架構143
InfoQ企業軟件開發叢書《架構之美》精選版
86
switch (type) {
<!-- note FBJS's Ajax object also implements AJAX.JSON and AJAX.FBML,
omitted
for brevity -->
case Ajax.RAW: ajax.ondone = function(data) {
document.getElementById('ajax1').setTextValue(data);
};
break;
};
ajax.post('http://www.fettermansbooks.com/testajax.php?t='+type);
}
</script>
FBML和我們的FBJS修改動作將這些輸入轉變成了例6-31中的HTML。這個例子中的
NOTE注釋指出了每種需要的轉換,不是實際輸出的一部分。
例6-31:HTML和JavaScript輸出的例子
<!-- NOTE 1-->
<script type="text/javascript" src="http://static.ak.fbcdn.net/
.../js/fbml.js"></script>
<!-- Application's HTML -->
These links demonstrate the Ajax object:
<br>
<!-- NOTE 2 -->
<a href="#" οnclick="fbjs_sandbox.instances.a1234567.bootstrap();
return fbjs_dom.eventHandler.call(
[fbjs_dom.get_instance(this,1234567),function(a1234567_event) {
a1234567_do_ajax(a1234567_Ajax.RAW);
return false;
}
,1234567],new fbjs_event(event));return true">
AJAX Time!</a>
<br>
<div>
<span id="app1234567_ajax1" fbcontext="b7f9b437d9f7"></span><!-- NOTE 3
-->
</div>
<!-- Facebook-generated FBJS bootstrapping -->
<script type="text/javascript">
var app=new fbjs_sandbox(1234567);
app.validation_vars={ <!-- Omitted for clarity -->};
app.context='b7f9b437d9f7';
app.contextd=<!-- Omitted for clarity -->;
app.data={"user":8055,"installed":false,"loggedin":true};
app.bootstrap();
</script>
144 第6章
InfoQ企業軟件開發叢書《架構之美》精選版
87
<!-- Application's script -->
<script type="text/javascript">
function a1234567_do_ajax(a1234567_type) { <!-- NOTE 3 -->
var a1234567_ajax = new a1234567_Ajax();<!-- NOTE 3 -->
a1234567_ajax.responseType = a1234567_type;
switch (a1234567_type) {
case a1234567_Ajax.RAW:
a1234567_ajax.ondone = function(a1234567_data) {
a1234567_document.getElementById('ajax1').setTextValue(a1234567_data);
};
break;
};
<!-- NOTE 4 -->
a1234567_ajax.post('http://www.fettermansbooks.com/testajax.php?t='+a1234
567_type);
}
</script>
下面是這段代碼中的NOTE的解釋:
NOTE 1
Facebook需要包含它自己的特殊JavaScript,包括fbjs_sandbox的定義,目的是渲
染開發者的腳本。
NOTE 2
還記得前面FBML初始化流程中的$rewrite_attrs元素嗎?FBML會重寫這個列表中
的屬性,變成Facebook特有的功能;這實際上是FBJS的一部分。所以這里的onclick
會激活這個頁面的其他元素,這些元素在用戶執行這個動作之前是非激活的。
NOTE 3
請注意在HTML和腳本中的元素如何加上了該應用的應用ID作為前綴。這意味著開
發者對alert()的調用將變成對app1234567_alert()的調用。如果Facebook的
后臺JavaScript在這個上下文中允許這個方法,它將最終轉向執行alert()。如果
不允許,這將是未定義的調用。類似地,這種加前綴的方式實際上為DOM樹提供
了命名空間,所以對該文檔某些部分的改變只限于開發者定義的那些部分。類似的
沙盒技術也允許開發者提供限制范圍的CSS。
NOTE 4
Facebook提供了一些專門的JavaScript對象,如Ajax和Dialog,目的是支持(并且常
常改進了)常見的使用場景。例如,通過Ajax()對象發出的請求實際上能獲得
FBML作為結果,所以它們被重定向到Facebook域的一個代理上,在這里Facebook
完成在線的FBML到HTML的轉換。
數據增長:Facebook平臺的架構145
InfoQ企業軟件開發叢書《架構之美》精選版
88
支持FBJS需要對FBML進行改動、專門的JavaScript和AJAX代理這樣的服務器端組件,
才能夠繞過應用Web架構的一些限制,但結果是很強大的。開發者因此可以享受絕大多
數的JavaScript功能(甚至改進了這些功能,如支持FBML的AJAX),而且平臺確保了應
用內容提供了用戶在Facebook上期望的受控體驗,這完全是通過技術手段來實現的。
6.5.3 服務改進小結
解決了新的n層社會關系應用的概念帶來的剩下一些問題之后,我們又改進了服務架構,
添加了COOKIE和FBJS等項,如圖6-6所示。
圖6-6:Facebook平臺服務
隨著開發者的社會關系應用越來越成為Facebook使用的一項集成服務,而不是由瀏覽器
使用的外部站點,我們已經重新創建或重新設計了瀏覽器的某些功能(通過平臺cookie、
FBJS等)。在試圖改變或重建“應用”的概念時,這是必需的兩個重要修改的例子。
146 第6章
App服務
SQL對象
App邏輯層
隱私規則FB邏輯層
FBML解釋
器+ FBJS
App顯示層
FB顯示層
App基礎設施
FB基礎設施
瀏覽器
App數據
FB數據
FB服務
InfoQ企業軟件開發叢書《架構之美》精選版
89
Facebook平臺包括類似的其他一些架構上的巧妙設計,這里沒有詳細介紹,其中包括數
據存儲API和瀏覽器端Web服務客戶端。
6.6 總結
Facebook的用戶貢獻的社會關系有效地提高了http://facebook.com上幾乎所有頁面的效
用。而且,這種數據非常通用,所以當它與外部開發者的應用棧結合在一起時,它的最
佳使用就出現了,這都是通過Facebook平臺的Web服務、數據查詢服務和FBML等技術
來實現的。從取得用戶的朋友或簡介信息的簡單內部API開始,我們在本章中詳細介紹
的全部改進展示了如何協調不斷擴展的數據訪問方法和容器網站的預期,特別是對數據
隱私和站點體驗集成方面的要求。每次對數據架構的新改動都發現了Web架構的一些新
問題,我們又通過對數據訪問模式的更強改進來解決這些問題。
雖然我們將關注重點完全放在那些使用Facebook的社會關系數據平臺的應用的潛力和約
束上,但像這樣的新型數據服務不一定局限于社會關系信息。隨著用戶貢獻和使用的信
息越來越多,這些信息在許多容器站點上都很有用(如內容收集、評論、位置信息、個
人計劃、協作等數據),各式各樣的平臺提供者可以應用Facebook平臺特有的數據和
Web架構背后的這些思想,并從中獲益。
數據增長:Facebook平臺的架構147
InfoQ企業軟件開發叢書《架構之美》精選版
90
1
總結
- 上一篇: 微信清除缓存的两种方法
- 下一篇: 编程积木