2010「架構師接龍」問答--楊衛華VS趙劼(轉)

add by zhj:雖然是幾年前的文章,但仍是頗有參考價值的html

原文:http://blog.zhaojie.me/2010/05/programmer-magazine-2010-5-architect.html程序員

上個月《程序員》雜誌向我約稿,但願我能夠參加5月份的「架構師接龍」欄目,我略爲猶豫了一下便答應了。「架構師接龍」是一個問答形式的欄目,每期由一我的提問,並由另外一我的回答。回答的一方即是下期的提問者。此次提問的架構師是新浪微博的技術經理楊衛華。他提出的問題包括語言選擇與架構設計、NoSQL存儲方案的取捨、微博類系統的架構等多個方面。楊衛華是國內技術社區一等一的高手,這使得我在回答問題時更有當心翼翼地班門弄斧之感。若是您對某些問題感興趣,也不妨來一塊兒討論一下。數據庫

語言選擇與架構設計

提問:不少架構師表示編程語言不重要,架構設計思想才重要,可是大部分團隊都是很是依賴某種語言的,甚至不少項目負責人也存在對某種語言存在偏好而對另一種語言反感的現象。你怎麼看待編程語言選型問題?同時業界也存在另一種現象,不少前沿技術研究者對一些新興語言如Erlang, Go等表示出狂熱,你對團隊或項目中是否引入這些新的語言持什麼觀點?編程

回答:在我看來,編程語言的選擇也是相當重要的。誠然,架構的設計思想可謂直接決定了系統自己的質量。與此相反,從理論上說,只要是圖靈完備的語言,就不存在「能力」上的根本差別,任何工做都是能夠實現的。可是,有一點也沒法被忽視,那即是咱們使用的語言,每每也會影響,甚至決定了咱們的思惟方式。後端

舉個有些極端的例子,若是人們還在使用匯編語言進行開發,那麼估計程序員的思惟永遠沒法跳出「子過程」這個抽象級別,什麼面向對象設計,函數式編程幾乎無從談起。人們在生產和學習過程種會引起一些需求,於是須要產生一些工具來輔助學習和生產,而「語言」即是此類工具之一。只有利用高級語言,人們纔能有效地把真實世界抽象成計算機這些機器盒子能認識的東西。數組

現在,可用於構建項目可選的主流語言每每都會有多種,有時候的確會發現,好像不一樣的語言──例如Ruby和Python──從各方面來講並無太大區別。這其實也是比較正常的,由於某些(甚至是大部分的)語言特性,並無對咱們的「思惟」產生影響。緩存

舉例來講,某些喜歡Ruby的朋友認爲Ruby語言的編程體驗很是良好,比如它的數組操做能夠直接作加法或減法:服務器

array = ['aaa', 'bbb', 'ccc'] - ['ccc', 'ddd'] 

或者說,在Python裏交換兩個變量的值也只須要一行代碼(大部分語言中可能須要藉助中間變量):架構

a, b = b, a 

可是,就我我的觀點來講,這些語言特性,雖然它們的確可讓編程工做變的相對輕鬆一些,例如可讓咱們少些一點代碼,但終究沒有改變,或是表現出另外一種編程思惟。這樣的語法特性,通常來講均可以經過構建一些簡單的輔助函數來作到相似程度的「簡化開發」(如上面Ruby的例子),對於那些非「即寫即拋」的程序來講,這些特性的優點並不明顯。併發

而與此相比,Ruby的Mixin機制和Python的Decorator就不只僅是「語法糖」,而是比較重要的語言特性了,由於它們能夠帶來或大大簡化某些十分有用的編程模式。

不過對於語言的選擇有時候還須要往更高處看。現今一些新出現,或者新流行起來語言,對於系統開發方面的影響則更爲深遠。舉個例子,目前提及併發/並行程序設計,沒法忽略的即是Erlang語言。這門語言提供了一種構建輕量級計算單元(在Erlang中被稱爲「進程」),使用發送消息(Message Passing)的方式進行相互間的通訊。這種作法避免了共享狀態(Shared State)方式下容易出現的各類問題,而且在其獨特的虛擬機實現下能夠獲得很強大的併發能力。可是,Erlang的任務調度機制有個特色,那即是它會爲每一個「進程」分配相同的計算能力。這樣,若是系統中有1000個進程,那麼每一個進程獲得的計算能力便會是100個進程時的十分之一。這種調度方式對於某些類型的應用來講可能並不合適,由於它可能在併發壓力增大的狀況下,形成吞吐量的下降甚至徹底中止服務(由於每一個任務都超時了)。Erlang的這個特性每每會直接影響到系統的架構方式。

不過在某些場景下,咱們也能夠選用其餘的語言。例如Scala,它一樣提供了基於Actor模型的消息傳遞併發機制。可是它的調度方式與Erlang不一樣(事實上因爲平臺功能限制,它也沒法實現Erlang的調度方式)。因爲Scala的Actor模型構建與JVM之上,所以它只能準備一個線程池,讓其中的線程不斷地處理消息的傳遞及處理任務,而額外的任務則會在隊列中等待。所以,Scala使用的並非Erlang那樣徹底公平的調度方式,可是這樣反而能夠優先處理先出現的任務,保證穩定的吞吐量。

所以,Erlang和Scala這兩種不一樣的調度機制,決定了它們適合不一樣的應用場景,或是系統架構的不一樣方式。我相信Facebook選用Erlang構建聊天平臺,Twitter選用Scala構建消息中間件都是有這方面考慮的。

固然,調度方式更像是由平臺決定,而不是語言決定的。不過在剛纔的特定問題上我認爲二者實際上是統一的。由於Erlang既是門語言,也表明了一個平臺。而Scala雖然是JVM平臺上衆多語言之一,但也只有它可以優雅的實現Actor模型的消息傳送機制。我始終認爲,一個語言特性只有真正「好用」,它才能被人們廣爲接受。例如,使用Java語言能實現Actor模型嗎?能,可是它缺乏Scala那樣靈活的函數式語法,以及模式匹配等特性,所以沒法構建出一個好用、易用的Actor框架,天然也就無人問津了。這其實也是「語言影響思惟方式」的典型案例之一。

異步及並行是現在系統構建不可或缺的因素。現在的新語言大都在這方面下了很大功夫。除了Scala和Erlang之外,在微軟.NET平臺上的新語言F#引入的創新特性「計算表達式(Computation Expression)」,使用相似於Monad的機制大幅度簡化了異步程序的開發難度。而JVM中的Clojure語言也引入了軟件事務內存(STM,Software Transactional Memory)。咱們幾乎能夠這麼說,現在每種新出現的語言都有獨特的「殺手級」特性,它們都是影響系統開發的重要因素,使用語言自己的支持能夠顯著下降系統的開發難度,增長可維護性和健壯性,這些並不是是由架構改進能夠輕鬆得到的效果。

現在「多語言」開發逐漸成爲一種趨勢,例如在Facebook的各個子系統分別使用了C、C++,Erlang,Java等多種語言/平臺,而後使用PHP做爲黏合劑鏈接起來。而Twitter也絕不例外地使用了Ruby,C,Scala和Java。如今的系統日趨複雜,幾乎沒有任意一種工具能夠徹底適合系統的所有開發,爲系統不一樣的組成部分選擇合適的語言,也是現在架構師須要面臨的挑戰。

與過去不一樣,如今即便肯定構建系統所用的平臺以後──如JVM,也會發現語言的選擇餘地會有不少,不一樣的語言的確也有不一樣的特性,能夠帶來一些特別的優點。例如,利用Ruby的動態特性,即可以方便地進行單元測試。而系統的生產部分代碼,可能即可以選用Scala等靜態編譯型語言,以便藉助更完整的靜態檢查工具來確保更加穩固的產品質量。

對於新語言的選取,不一樣風格的架構師會採起不一樣的策略。例如保守的架構師可能會根據語言所在社區是否活躍,語言相關資源是否豐富,相應的程序員是否容易招聘來考慮語言或平臺的選擇。這種作法是十分正常的。可是萬事都講究個平衡,在某些狀況下「保守」和「抱殘守缺」或「固步自封」之間僅一步之遙。

我我的的風格相對比較「激進」,十分樂於吸取和嘗試一些新事物。個人建議是,每一個技術團隊都應該挑選出幾個技術水平較高,經驗較爲豐富的成員,普遍的吸取新事物的發展,並在合適的時候向團隊及生產環境作出提案,以改進系統的開發效率或者質量。由這些高級技術人員進行引導,每每能夠較好的預估新技術對於產品的影響,即使出現了一些問題也能夠設法自行解決。

據我瞭解,一些較爲活躍的技術團隊,尤爲是一些Web 2.0產品的技術團隊,在這方面都有比較好的實踐。

NoSQL存儲方案的選型

提問:最近不少公司有向NoSQL方向發展趨勢,不少架構師也關心是否須要將關係數據庫轉向NoSQL,請問能給正在選型的架構師哪些建議?

回答:個人我的見解是,NoSQL自己是好東西,可是在使用的氛圍方面稍有一些扭曲。多是受到關係型存儲方式的「壓抑」過久,現在冒出一個NoSQL運動讓人眼前一亮,不禁得熱血沸騰起來。

NoSQL的出現,本來不是爲了徹底取代關係型數據庫,而是爲了應對關係型數據庫在性能和伸縮性方面的缺陷而提出的存儲方式。NoSQL不該該是「No SQL」,更爲穩當的方式應該是「Not Only SQL」。

放眼現在比較成功的NoSQL應用,彷佛除了Google因爲數據規模,資源沉澱等緣由以外,其餘系統大都是將NoSQL做爲一種優化的手段在使用,而並不是是做爲系統的主要存儲方式,它們主要使用的存儲方式依然是MySQL等關係型數據庫。而事實上,各系統也是在架構演變過程當中,發現關係型數據庫成爲了系統優化的瓶頸,進而在必定程度上引入NoSQL存儲方式以改善性能。

例如就在不久以前,SourceForge宣佈將在系統中引入MongoDB,而Twitter也打算開始使用由Facebook建立的Cassandra。可是以SourceForge與Twitter目前基於關係型數據庫所支撐起來的規模,也已是無數系統難以企及的目標了。更況且,如Stack Overflow這樣號稱全世界最大的程序員網站,做爲存儲後端也只是使用了單臺關係型數據庫。

畢竟,關係型數據庫的性能並不是差到不可接受,NoSQL的優點也只有在達到必定規模時才能體現出來。並且,除了存儲方式之外,系統中能夠優化的地方還有不少。例如最傳統的,緩存,一個實現較好的緩存機制能夠減小95%以上的數據庫訪問,這對於系統性能的影響也是至關巨大的。

在目前,使用NoSQL存儲方式的另外一個不便之處即是工具的缺失。我在項目中也使用了MongoDB,一個十分明顯的體會即是,對於MongoDB的操做比關係型數據庫要麻煩很多。例如在訪問關係型數據庫時能夠利用現成的映射工具,通過多年發展此類工具也已經變得很是靈活、高效,可以應對絕大部分的使用場景。而在使用MongoDB時,我彷佛又有了回到當年裸寫JDBC的感受了,甚至對於某些平臺來講,連一個成熟的驅動(例若有鏈接池的支持)都須要親自動手開發一個。對於一個有經驗的開發人員來講,便寫一些「夠用」的代碼天然不是難事,不過這的確也是一件會影響投入產出比的事情。

此外,NoSQL雖然性能高,但這也是經過在必定程度上犧牲數據的完整性或一致性的保證換來的,傳統的關係型數據庫卻在這方面投入了很大的精力,例如事務機制,雖然會下降性能,可是卻保證了數據的一致性。可是,現在的NoSQL存儲幾乎都沒有提供相似的機制(畢竟有個沒法迴避的CAP規律),這樣多個相關的操做一旦中斷(例如出現了異常),則很容易形成數據「此長彼短」的現象。並且,現在的NoSQL產品出於性能考慮,幾乎無一例外會帶有必定程度的緩存機制,不會將新建或更新的數據直接寫入磁盤。所以,若是沒有一個集羣環境,在遇到突發情況時則極可能帶來數據丟失的狀況。對此,如MongoDB明確指出,它對於單機的持久性並不十分重視,它的設計人員以此換來更爲重要的參數:性能。這意味着,一旦使用NoSQL做爲主要存儲方式,則每每會須要同步跟進一些周邊措施,例如可能要在保證數據的最終一致性方面投入較多的精力。

當咱們肯定要選擇NoSQL存儲方式的時候,則必須根據本身的業務特徵選取特別的NoSQL產品。目前NoSQL主要分爲四大類:BigTable,Key-Value,文檔型,及圖數據庫。它們有各自的性能優點及適用範圍。例如Key-Value存儲方式支持的查詢方式很是有限,可是因爲結構簡單,它的性能和伸縮性可謂傲視羣雄。而文檔型數據庫,如MongoDB,它所支持的查詢方式很是靈活,並內置Map Reduce機制,能夠直接輸入JavaScript腳本進行一些特殊的數據處理及彙總。而如Neo4j這樣的圖數據庫,因爲直接支持「節點」、「(有向)關係」等概念,則對於一些關係型數據庫、文檔型數據庫難以應對或建模的查詢或遍歷方式(如最短路徑計算),就有了很是直接、天然且高效的支持。

總之,架構師選擇的不是SQL或NoSQL自己,而應該只是「最合適」的東西。

關注的方向和領域選擇

提問:不少架構師都喜歡學習Google, Facebook等大型系統的經驗,但另外很多架構師則認爲絕大多數網站都不會成長成一個「大型網站」。絕大多數工程師都沒有能力創建和維護一個相似GFS的系統。對絕大多數網站而言,把時間耗在所謂「大型網站」的架構上沒有意義,你怎樣看待這種說法,架構師應該如何選擇關注的方向和領域?

回答:我在這方面的見解是,雖然Google,Facebook等大型系統的規模對於絕大多數人來講多是永遠沒法接觸到的,可是它們的經驗及措施可能會給咱們帶來一些其餘方面的體會。

例如,Map Reduce本來是函數式編程中再普通不過的概念和手段,可是Google將它和GFS等其餘基礎設施一結合,便成爲了一個無比強大的分佈式計算技術。可是,Map Reduce它自己仍是十分簡單的東西(Google Map Reduce實現的複雜點主要仍是在於GFS),它也並不是Google專有的東西,咱們受到這樣的啓發也能夠將其用到別處。例如在MongoDB和CouchDB中都內置了MapReduce支持,在去年的QCon Beijing大會中,FreeWheel公司在它們的廣告平臺中,也使用了本身的方式實現了Map Reduce計算機制。

所以,即使是沒法成爲真正的巨人,也能夠關注巨人成長過程當中所吸收的經驗教訓,從中也能夠獲得一些啓發。即便是看成一個有趣的故事去了解也好,即便是爲了打開眼界也好。有時,咱們須要的可能只是一個不經意的提示。

微博類產品的架構難點

提問:目前國內不少互聯網門戶都在作微博產品,你以爲微博技術架構的主要難點在哪些方面?

回答:從複雜度上面來講,微博產品的業務是相對比較簡單的,我認爲它在技術架構上有兩大要素:消息傳遞與緩存。

微博產品從產品性質上來講幾乎徹底就是一個消息分發平臺,所以一個良好的消息傳遞機制是相當重要的。當一個用戶發出消息以後,它能夠被許多人觀察到。對於一個名人來講,被數十萬用戶所追隨是一件很是普通的事情。那麼此時,若是指望全部的追隨者都即時地看到消息這幾乎是件不可能的事情,所以在實現時咱們每每須要構建一個消息隊列,將消息快速地派送至隊列中等待處理,最終將這條消息「陸續地」顯示在每一個追隨者的時間軸上。這裏勢必會產生延遲,但對於業務質量來講並不是不可接受。但顯然這個延遲也不能夠太長,在Twitter上這個平均延遲是500毫秒,從絕對數值上看並不算過短,但也已夠用。Twitter在這方面的處理方式是利用Scala的Actor模型及Apache Mina編寫了一個分佈式的消息傳輸框架Kestrel,它具備快速、輕量(包括註釋纔不到2000行代碼)、持久、穩定等特性,但不具有事務性,也不保證消息的順序處理。所以能夠這麼說,Kestrel是一個Twitter根據自身需求「定製」出的消息傳輸機制。

另一個要點即是每一個大型系統都不可或缺的緩存機制。有人說緩存就比如萬能膏藥,哪兒不舒服就再哪兒擦點便能見效。這話有必定道理。如Twitter便設計了相對複雜的多級緩存機制,幾乎對於每一個IO密集的地方都進行了緩存。例如記錄ID序列的向量緩存(Vector Cache),紀錄每條消息等具體內容的行緩存(Row Cache)。此外,因爲它的API訪問量很大,僅僅是從消息內容轉化成API的輸出形式(可能只是一些字符串鏈接操做)也會消耗較多代價,所以Twitter還爲消息的API輸出形式設計了片段緩存(Fragment Cache)。最後天然還有對某些熱門頁面的頁面緩存(Page Cache)。除了頁面緩存以外,其餘緩存的命中率都在95%以上,可見緩存機制對於Twitter系統的重要程度。值得注意的是,向量緩存及行緩存都是Write Through的,這意味着基本上全部的新數據都在緩存中存在一份拷貝。正如Twitter的Evan Weaver在QCon London 2009會議上講的那樣:Everything runs from memory in Web 2.0。

最後,對於微博類應用來講,可能會由於某個突發事件形成訪問量暴增的現象,如何抵抗住消息轟炸也是一個重要的課題。例如Twitter使用了雲計算的方式來應對此類問題,在須要的時候它便會租用更多的計算資源。不過增長服務器只是純硬件的投入,而架構設計可否順利且充分地利用起新增的設備也是一個值得關注的地方。從這個角度看,一個高效的分佈式消息傳遞機制在這裏會扮演重要的角色。若是有了合適的消息機制,首先可以將消息負載較爲容易地平衡至多臺服務器上,其次即便是在壓力增大的狀況下,響應時間雖然會按線性增加,可是系統的吞吐量仍是能夠保持在正常的水平。

給將來架構師的建議

提問:不少工做2-3年的軟件工程師談到職業規劃都是但願往架構師方向發展,請問能給這些正在成長的工程師哪些建議?如何才能成爲一個優秀的架構師?

回答:其實我也不知如何給出一些有效而具體的建議。我認爲架構師不是一個職位或是職責,而更像是一種思惟方式。其實只要打開眼界,不斷吸取和關注技術及業務的發展,待積累到一個合適的時候即可以對系統架構提出本身的思路及建議的時候,那你就是一個架構師了。

其實每一個程序員均可以是架構師。

相關文章
相關標籤/搜索