關於問答類的應用,最早接觸的是stackoverflow和知乎 ,而Quora作為知乎的原型,因為其創始人來自FaceBook而吸引了我。事實上關於Quora的技術分析,馮大輝和陳皓都已經有所詳細的闡述:《Quora 用了哪些技術 ?》《Quora使用到的技術》。通過他們的文章,我看到了一篇更詳細的說明《Quora’s Technology Examined》。看完以后感覺有很多東西值得深入的去學習和整理。於是決定將這篇文章先翻譯出來,作為后面web學習的引子吧。下面開始吧:
Quora因為其流暢的系統已經給IT創業界掀起了一場風暴。Quora為什么這么給力呢,除了有大量聰明的提問者和回答者的支持外,更因為其來自FaceBook的聯合創始人精心設計的后端系統;
所有的聰明人都在使用這個智能的工具,這一點不足為奇。但是有很多人都在思考,Quora為什么能運行的如此流暢呢。NoSQL 的研究者們撓着頭問道“為什么 Quora 使用 MySQL作為其數據存儲,而不是像 Cassandra, MongoDB, CouchDB等這些NOSQL工具?“.
[Adam D'Angelo(Quora創始人)的回答:]
1、如果你在應用層進行分區,MySQL的可擴展性不是問題。FaceBook報道說在2008年的時候運行的MySQL服務器有1800台,而只有2個DBA。你不能跨分區做連接,但是NoSQL數據庫無論如何也做不到。Facebook 還未確認使用Cassandra 作為其住數據源,而是可能僅僅用於收件箱搜索業務。
2、這些分布式數據庫像Cassandra,MongoDB,CouchDB實際上還不是很穩定和可擴展。Twitter 顯然一年前就試圖從MySQL 遷移到Cassandra 。如果有人報道說使用這些系統中的一個作為主存儲庫超過1000太機子,超過1年,我就會重新考慮我的選擇。
<< 2011.8更新: 當我寫了這些后,foursquare 報道了一次11個小時的因為MongoDB引起的宕機事件。另外,一個創業的朋友,其用戶量正處於爆炸式增長期,想轉到MongoDB ,但因為其不穩定,一個月后放棄了。Twitter 也放棄了往Cassandra 遷移。Facebook 正在脫離Cassandra,HBase還不錯,不過如果你沒有相應深入理解人員,仍然是一種冒險 >>
3、應用的在線數據存儲部分是最不應該冒險嘗試新技術的地方。一旦你丟失了你的數據庫或出現了故障,這將是一次無法恢復的災難。If you're not the developer of one of these new databases, and you're one of a very small number of companies using them at scale in production, you're at the mercy of the developer to fix bugs and handle scalability issues as they come up.
4、其實你可以用單個MySQL數據庫走得更遠,而不用擔心在應用層分區。你可以縱向擴展你的機器,通過增加內核和內存。如果你在數據庫前有一套緩存系統(則更容易進行擴展),你只要考慮寫入就夠了。你可以使用S3或一些其他的分布式哈希表存儲那些最大的對象。你不必承擔未來10倍系統規模需求,只要你有信心在規模增長的時候可以進行擴展。
5、很多對大量MySQL機器進行人工分區的問題可以通過構建一層位於應用層與MySQL之間的數據訪問層(實現數據庫負載的自動分散)就可以大大緩解。FriendFeed 就是一個實現這一技術的很好的例子。
6、Personally, I believe the relational data model is the "right" way to structure most of the data for an application like Quora (and for most user-generated content sites). Schemas allow the data to persist in a typed manner across lots of new versions of the application as it's developed, they serve as documentation, and prevent a lot of bugs. And SQL lets you move the computation to the data as necessary rather than having to fetch a ton of data and post-process it in the application everywhere. I think the "NoSQL" fad will end when someone finally implements a distributed relational database with relaxed semantics.
在這篇博文中,我將根據這些可用的關於Quora 的信息片斷,從技術的角度深入研究Quora 。他們究竟做了哪些技術決策?他們的技術架構究竟是怎么樣的?他們使用了哪些框架和語言?他們是如何做到使查詢這么快捷?
Quora的組成部分
Quora大體有如下及部分功能…
- 你可以提問題
- 你可以解答問題 (如果你想匿名也可以)
- 你可以對已回答的問題進行評論
- 你可以頂或者踩這些問題的回答
- 問題可以被歸類為各個主題
- 你可以寫一個 post(a informative statement, rather like a orphaned answer or blog post)
- 你可以follow問題,主題或其他用戶
- 在頂部有一個反應超快的能自動完成(錄入內容)的搜索框,可以加倍問題錄入速度
最后一點,超快自完成搜索框是Quora的一個典型特征。當你開始輸入一個問題的時候,你可以立即看到是否有其他人已經問過這個問題或已經存在這個主題或post了。
引擎蓋下面究竟有些什么?
搜索框
只有問題,主題標簽,用戶名字或post標題被索引並提供搜索。並不支持全文索引,因此無法搜索問題內容和答案。被索引的文本都做了標記化,所以即時單詞順序不同,依然可以匹配。通過前綴匹配技術,在輸入完整個詞之前就可以看到最匹配的結果。例如,敲“mi”可以立即看到“Microsoft”。
其搜索還會有一些非常簡單的模糊匹配的算法,因為“nears”可以匹配“near”,但是“pony”與“ponies”無法匹配。“主題別名”允許相似的主題名進行匹配,如“startup”和“start-up”。當然,這些“主題別名”需要人工事先輸入,否則無法匹配。
另外,如果有重復的問題,其中一個問題會自動跳轉到另一個問題(Quora的一個亮點),但是在搜索中還是會出現。由於沒有n-gram索引算法,輕微的拼寫錯誤將導致不匹配,例如:搜索“google”的時候如果輸入“gooogle”,將無法找到。
原先他們使用了一個開源的搜索服務器,叫Sphinx。其支持上述的那些功能。現在他們因為受實時性方面一些限制放棄了。新的解決方案是他們自己開發的,在前綴索引和匹配控制方面得到了提升,這個算法由Python實現。
[Adam D'Angelo(Quora創始人)的回答(Nov 13, 2010):]
我們的搜索引擎是自己寫的。除了Thrift和Python’s unicode library(解決unicode問題),沒有用到其他庫.
快捷的查詢
就像我剛才提到過的,search-box得快嗎?我做了一些測試發現其響應速度讓我印象深刻。問題通過AJAX發送一個GET request。Responses 返回封裝了HTML的JSON包。可能是因為需要做關鍵字匹配及高亮顯示,其解析JSON是在服務端,而不是在客戶端用JavaScript,因為這對JavaScript來說太過復雜了。例如:敲入“categories” ,可以使關鍵字“category”高亮顯示;
我通過Linode的機器每50毫秒進行一次查詢,並觀察其響應。Quora 並沒有對你發送的requests進行“節流”。在瀏覽器上,我發現敲入“Microsoft”9個字符會向Quora的搜索服務器發送9次請求,而與你的敲入速度幾乎沒有關系。正像你后面會看到的,服務端會進行控制,所以如果后端過負載的時候,它可以減少更新結果的頻度,而這一切都無需修改前端的JavaScript。
Quora使用長連接,當你開始敲入查詢內容的那刻就會建立一個HTTP連接。這個連接一致保持着,並用於進一步的http請求。如果60秒沒有使用,這個連接才會關閉。當再次輸入的時候,新的連接又會建立。
為了模擬在搜索欄中輸入一個單次,我發送了下面的請求,例如“butler”是6個request(“b”, “bu”, “but” … “butler”).
"butler" (6 chars) duration: 0.393 secs 0.065 secs per query "butler monkeys" (14 chars) duration: 0.672 secs 0.048 secs per query "fasdisajfosdffsa" (16 chars) duration: 0.746 secs 0.046 secs per query
最后一個查詢是用來測試:如果在緩存中沒有的內容是否會使整個搜索變慢的。我發現沒有變慢。這意味着:一種情況是這時他們沒有使用緩存,緩存僅僅是用來減輕后端搜索引擎的負載的。還有一種情況是他們使用了一種智能的處理(例如:如果沒有“fasd”的匹配結果,就不對“fasdi”做匹配處理了,直接中止)
Quora打算實現全文檢索嗎?
[Adam D'Angelo(Quora創始人)的回答(Sep 1, 2010):] 最終會的,我還沒有實現是因為我們優先處理其他方面的事情,但是未來我們肯定會實現的。
Webnode2 And LiveNode
Webnode2 和LiveNode 是Quora的內部系統,用來生成和管理內容。Webnode2 生成HTML,CSS和JavaScript ,它和LiveNode緊密結合。LiveNode是負責管理web頁的內容顯示。Charlie Cheever說如果他可以從新開始,他 第一件事要做的就是重寫整個LiveNode.
Quora的工程師看上去對他們搞的這些東西非常的滿意,並且 他們也在努力地找到這些東西的弱點。有一個有意思的關於LiveNode的問題是,如果A和B同時正在看相當的一個問題,那么用戶A的一些交互動作會影響B的頁面。例如,如果A頂了一下某個答案,那么這個答案可能會往上移動。這樣的一個顯示變化會通過AJAX更新B的瀏覽器。如果B此時展開了評論,可能會受到影響。
LiveNode 由這些東西寫成:Python, C++, and JavaScript. jQuery ,Cython也用到了。
因為Quora 想要對他們的LiveNode開源並准備把他們的代碼分開,做這個事可能需要太多的工作和時間。
Charlie Cheever 指出 WebNode2 和 有一個叫做 “free and easy website builder” 的 Webnode 的 webnode.com沒有任何的關系。
Amazon Web Services
Quora的主機都在Amazon EC2 和S3 上。當然從長期來說這不如運行自己的服務器來得划算,但是對像Quora這樣的快速成長的公司來說簡直是完美的設計。
Ubuntu Linux
Quora 使用Ubuntu Linux 作為他們的操作系統選擇。沒有什么值得驚訝的,因為Ubuntu Linux在Amazon EC2上的管理和部署很容易。Adam D’Angelo指出他在高中和大學的時候都使用Debian Linux ,為什么換成ubuntu呢?看Adam D’Angelo怎么說的:“it works and there hasn’t been a compelling reason to switch”.
靜態內容
你只要看一下Quora 任何一個頁面的HTML代碼,就會看到他們正在使用Amazon的分布式內容分發網絡,Cloudfront。URLS是這樣的:
http://d2o7bfz2il9cb7.cloudfront.net/main-thumb-670336-25-7kmigSSkkdusoE6gHRkdQsXfjuTCaxQs.jpeg
CloudFront 用於所有靜態的圖片、CSS和JavaScript (除了Google的Analytics JavaScript,是在Google的主機上的)。圖片上傳到EC2的機器上,然后調整尺寸后上傳到S3。這是通過Python S3 API進行管理的。
HAProxy 負載均衡
Quora在第一線使用了HAProxy,作為負載均衡器,而后面是Nginx。
Nginx
Nginx作為反向代理服務器,部署在在負載均衡器后面。如果想知道更多關於這一方面的信息,我推薦讀:“Using Nginx As Reverse-Proxy Server On High-Loaded Sites“.
Pylons And Paste
Pylons, a lightweight web framework, is used as their main web-server behind Nginx. They use the default Pylon + Paste stack.
Pylons, 一個輕量級的web框架作為他們的主要web服務器,部署Nginx后面. 他們使用默認的 Pylon + Paste stack.
選擇Pylons,就像你在萬聖節選擇一個南瓜,他們去除了其內部的templates 和ORM,然后加入了自己的技術,用Python寫的。看這里LiveNode and webnode2 reside.
MochiMedia(一個在線游戲網站)也使用了 Pylons.
Python
因為Charlie 和Adam來自FaceBook, PHP應該說是一個很好的選擇。然而正如Adam 說的。“Facebook is stuck on that for legacy reasons, not because it is the best choice right now“。從這些經驗來看,他們知道技術選型,尤其是編程語言對公司長期發展非常重要。他們也考慮過C#,Java,和Scala。選擇C#則不僅僅是語言了。那需要他們引入微軟的一大堆東西。Python勝過Java的原因是它比java更富有表現力,也更容易快速寫出代碼。Scala則太年輕。Adam 提到速度和缺乏類型檢查是Python的弱點,但是他們都已經知道這個語言相當不錯。針對性能要求高的后端組件他們使用C++來寫,因為Python的速度不夠。他們覺得Ruby和Python很接近,但是他們有Pyhton 的經驗,對Ruby缺乏經驗,因此Python勝出了。他們使用的是Pyhton 2.6。
使用Python的另一個好處是Python的數據結構和JSON可以很好的映射起來,代碼易讀性很高。而且有很多的庫,調試器和重載器。Quora瀏覽器和服務器之間的通信主要使用JSON格式,因此這也成為一個重要因素。
沒有使用IDE,大部分開發者使用Emacs文本編輯器。顯然這是一個個人選擇,隨着團隊的壯大,也許會改變。
另外,他們提到了PyPy,一個讓 Python更快更靈活的項目。
Thrift
Thriftis used for communications between backend systems. The Thrift service is written in C++.
Thrift 用於后端服務器間的通訊。Thrift 服務由 C++開發。Facebook同樣使用了這個技術。
[Adam D'Angelo(Quora創始人)的回答(Sep 4, 2010):]
主要是如果你想將請求間的數據保存在內存中,並使你的Pyhton代碼無狀態。需要寫一個Python封包,封裝一個C庫,涉及一些基於引用計數的內存管理。這需要清楚Python內核的人,但是寫一個thrift 接口則是非常的簡單。同時通過這種方法,你也隔離了異常,不至於使Python代碼崩潰。
Tornado
Tornadoweb框架用於實時更新。這是他們的Comet服務器,用於處理大量的需要長時間poll和push更新的網絡連接。
Long Polling (Comet)
Quora顯示的不僅僅是靜態頁面。當其他人提交了像提問、回答、評論這些新的內容時,每個頁面都要更新。就像Adam D’Angelo 說的,當前處理這種情況最好的技術就是“long polling”。和“polling”不同,傳統的“polling”,瀏覽器需要重復的發送請求到服務器:“有更新的嗎?”,然后服務端回答:“沒有”。幾秒鍾后,瀏覽器又來問:“那現在呢?”“沒有”,“那現在呢?”“還是沒有!”。這種模式將瀏覽器作為驅動方。這是弊端,因為瀏覽器並不知道需要等待多久再去輪詢。如果瀏覽器輪詢過於頻繁,將過度增加服務端的負載。如果輪詢頻度不夠,當有新的內容時,服務端只能在那里等待客戶端來輪詢,用戶將無法及時看到更新內容。
“Long polling”,也叫“Comet”,這種模式將服務端作為控制端,客戶端等待響應。客戶端和服務端的會話是相同的,不同的是原來客戶端在做下一次輪詢前只能等待,現在是服務端等着做出響應。服務端在等待是否有更新到來時,在很長一段時間(如:60秒)內保持連接。當更新到來時,服務可以立即做出響應。當客戶端接收到更新響應的時候,可以立即發起一個新的請求。服務端再一次延遲響應,直到有更新返回或時間到期沒有響應。
long-polling的優點是減少了前后端交互的次數。讓服務器端來控制時間,所以,內容更新可能會只是幾個毫秒。這也使其用於處理聊天應用或者那些真正需要實時更新的應用。
但是,這種模式的不足之處是——這會讓服務器端出現大量的TCP鏈接,想一想,Quora也快是百萬級用戶的應用了,只需要10%的在線用戶,你就需要一個可以處理10萬並發量的架構。注意,如果一個用戶在其瀏覽器里打開了多個Quora網頁的話,那么,這個鏈接器會是非常致命的。
當然,好的消息是已經有一些技術專門為Long Polling設計,這些技術可以讓你在那些等待的連接中只會消耗非常非常少的內存(因為那些等待連接並不需要所有的資源)。例如:Nginx 是一個單線程的事件驅動的小型服務器,每一個鏈接只花非常小的內存。每一個Nginx的進程只會在一個時候處理一個連接。這意味着其很容易擴展成一個可以處理成千上的並發量的服務架構。
Adam D’Angelo, Quora (Sep 29, 2010)
There is no reliable way to do this without having the client polling the server. However, you can make the server stall its responses (50 seconds is a safe bet) and then complete them when a message is ready for the client. This is called “long polling” and it’s how Quora, Gmail, Meebo, etc all handle the problem.
If you have a specialized server that uses epoll or kqueue, you should be able to hold on the order of 100k users per server (depending on how many messages are going). This is called the “c10k” problem. http://www.kegel.com/c10k.html
MySQL
就像Adam D’Angelo 的老雇主FaceBook一樣,Quora 重度使用了MySQL。在Quora上的一個問題的回答中“When Adam D’Angelo says “partition your data at the application level”, what exactly does he mean?“, D’Angelo深入的講述了在分布式存儲的情況下如何使用MySQL(或者說關系型數據庫)
最基本的建議是按需對數據進行分區,如果可能盡量把數據存儲在一台機器上,並且使用一個帶主鍵的hash表對橫跨多個數據庫的大數據集進行分區。必須避免表連接。對此,他認為FriendFeed的架構是一個很好的例子。FriendFeed的架構在Bret Taylor 的文章“How FriendFeed uses MySQL to store schema-less data“中有所闡述。D’Angelo 也說道,你不應該在一個社交網站中使用NOSQL 數據庫,除非你有上百萬的用戶。
不僅僅Quora和FriendFeed 側重使用MySQL,是否聽說過“Google”?真的很難想像,在the words of Google上是這么說的:“Google使用MySQL在一些和搜索無關的應用上”。Google已經為MySQL發布了與復制,同步,監視和提升速度等方面相關的補丁。
Adam D’Angelo, Quora (Oct 10, 2010)
One option is to simulate some load. Write a script that mimics the kinds of queries your application will be doing, and make sure it can handle the amount of load you want it to be ready for (especially as the size of the dataset changes).
Memcached
Memcached用於MySQL的緩存層.
Git
Git用作版本控制.
JavaScript Placement
如果你看過Quora的頁面代碼,你會看到JavaScript 代碼都在頁面的底部。Charlie Cheever建議這會讓你的頁面顯得載入得很快,因為其先顯示內容,然后在載入Javascript。
Charlie Cheever 遵循 “讓web站點最快的14條原則”
Steve Souders, 《高性能網站建設指南》一書的作者列出了以下原則: rules for making websites faster. Quora的聯合創始人 Charlie Cheever也提到過, 想必這也是Quora速度快的一個原因吧。
“One resource we used as a guide is Steve Souders’ list of rules for high performance websites:http://stevesouders.com/hpws/rules.php” – Charlie Cheever, Quora
Steve Souders’ 14 rules are…
1、Make Fewer HTTP Requests
2、Use a Content Delivery Network
3、Add an Expires Header
4、Gzip Components
5、Put Stylesheets at the Top
6、Put Scripts at the Bottom
7、Avoid CSS Expressions
8、Make JavaScript and CSS External
9、Reduce DNS Lookups Minify JavaScript
10、Avoid Redirects
11、Remove Duplicate Scripts
12、Configure ETags
13、Make AJAX Cacheable
結論
Quora是一個現代科技創業的偉大例子。他們的團隊非常小,但是對他們使用的技術理解非常到位。他們對已經選擇的技術做了深思熟慮,同時對於哪些組件選擇既有產品還是自己重新開發有一個非常好的洞察力。他們似乎渴望將他們內部使用的技術分享到開源社區,對此我將持續關注。