.NET技術+25台服務器怎樣支撐世界第54大網站
英文原文:StackOverflow Update: 560M Pageviews A Month, 25 Servers, And It's All About Performance
StackOverflow 是一個 IT 技術問答網站,用戶可以在網站上提交和回答問題。當下的 StackOverflow 已擁有 400 萬個用戶,4000 萬個回答,月 PV5.6 億,世界排行第 54。然而值得關注的是,支撐他們網站的全部服務器隻有 25 台,并且都(dōu)保持著(zhe)非常低的資源使用率,這(zhè)是一場高有效性、負載均衡、緩存、數據庫、搜索及高效代碼上的較量。近日,High Scalability 創始人 Todd Hoff 根據 Marco Cecconi 的演講視頻“ The architecture of StackOverflow”以及 Nick Craver 的博文“ What it takes to run Stack Overflow”總結了 StackOverflow 的成(chéng)功原因。
意料之中,也是意料之外,Stack Overflow 仍然重度使用著(zhe)微軟的産品。他們認爲既然微軟的基礎設施可以滿足需求,又足夠便宜,那麼(me)沒(méi)有什麼(me)理由去做根本上的改變。而在需要的地方,他們同樣使用了 Linux。究其根本,一切都(dōu)是爲了性能(néng)。
另一個值得關注的地方是,Stack Overflow 仍然使用著(zhe)縱向(xiàng)擴展策略,沒(méi)有使用雲。他們使用了 384GB 的内存和 2TB 的 SSD 來支撐 SQL Servers,如果使用 AWS 的話,花費可想而知。沒(méi)有使用雲的另一個原因是 Stack Overflow 認爲雲會(huì)一定程度上的降低性能(néng),同時(shí)也會(huì)給優化和排查系統問題增加難度。此外,他們的架構也并不需要橫向(xiàng)擴展。峰值期間是橫向(xiàng)擴展的殺手級應用場景,然而他們有著(zhe)豐富的系統調整經(jīng)驗去應對(duì)。該公司仍然堅持著(zhe) Jeff Atwood 的名言——硬件永遠比程序員便宜。
Marco Ceccon 曾提到,在談及系統時(shí),有一件事(shì)情必須首先弄明白——需要解決問題的類型。首先,從簡單方面(miàn)著(zhe)手,StackExchange 究竟是用來做什麼(me)的——首先是一些主題,然後(hòu)圍繞這(zhè)些主題建立社區,最後(hòu)就(jiù)形成(chéng)了這(zhè)個令人敬佩的問答網站。
其次則是規模相關。StackExchange 在飛速增長(cháng),需要處理大量的數據傳輸,那麼(me)這(zhè)些都(dōu)是如何完成(chéng)的,特别是隻使用了 25 台服務器,下面(miàn)一起(qǐ)追根揭底:
狀态
StackExchange 擁有 110 個站點,以每個月 3 到 4 個的速度增長(cháng)。
400 萬用戶
800 萬問題
4000 萬答案
世界排名 54 位
每年增長(cháng) 100%
月 PV 5.6 億萬
大多數工作日期間峰值爲 2600 到 3000 請求每秒,作爲一個編程相關網站,一般情況下工作日的請求都(dōu)會(huì)高于周末
25 台服務器
SSD 中儲存了 2TB 的 SQL 數據
每個 web server 都(dōu)配置了 2 個 320G 的 SSD,使用 RAID 1
每個 ElasticSearch 主機都(dōu)配備了 300GB 的機械硬盤,同時(shí)也使用了 SSD
Stack Overflow 的讀寫比是 40:60
DB Server 的平均 CPU 利用率是 10%
11 個 web server,使用 IIS
2 個負載均衡器,1 個活躍,使用 HAProxy
4 個活躍的數據庫節點,使用 MS SQL
3 台實現了 tag engine 的應用程序服務器,所有搜索都(dōu)通過(guò) tag
3 台服務器通過(guò) ElasticSearch 做搜索
2 台使用了 Redis 的服務器支撐分布式緩存和消息
2 台 Networks(Nexus 5596 + Fabric Extenders)
2 Cisco 5525-X ASAs
2 Cisco 3945 Routers
主要服務 Stack Exchange API 的 2 個隻讀 SQL Servers
VM 用于部署、域控制器、監控、運維數據庫等場合
平台
ElasticSearch
Redis
HAProxy
MS SQL
Opserver
TeamCity
Jil——Fast .NET JSON Serializer,建立在 Sigil 之上
Dapper——微型的 ORM
UI
UI 擁有一個信息收件箱,用于新徽章獲得、用戶發(fā)送信息、重大事(shì)件發(fā)生時(shí)的信息收取,使用 WebSockets 實現,并通過(guò) Redis 支撐。
搜索箱通過(guò) ElasticSearch 實現,使用了一個 REST 接口。
因爲用戶提出問題的頻率很高,因此很難顯示最新問題,每秒都(dōu)會(huì)有新的問題産生,從而這(zhè)裡(lǐ)需要開(kāi)發(fā)一個關注用戶行爲模式的算法,隻給用戶顯示感興趣的問題。它使用了基于 Tag 的複雜查詢,這(zhè)也是開(kāi)發(fā)獨立 Tag Engine 的原因。
服務器端模闆用于生成(chéng)頁面(miàn)。
服務器
25 台服務器并沒(méi)有滿載,CPU 使用率并不高,單計算 SO(Stack Overflow)隻需要 5 台服務器。
數據庫服務器資源利用率在 10% 左右,除下執行備份時(shí)。
爲什麼(me)會(huì)這(zhè)麼(me)低?因爲數據庫服務器足足擁有 384GB 内存,同時(shí) web server 的 CPU 利用率也隻有 10%-15%。
縱向(xiàng)擴展還(hái)沒(méi)有遇到瓶頸。通常情況下,如此流量使用橫向(xiàng)擴展大約需要 100 到 300 台服務器。
簡單的系統。基于 .Net,隻用了 9 個項目,其他系統可能(néng)需要 100 個。之所以使用這(zhè)麼(me)少系統是爲了追求極限的編譯速度,這(zhè)點需要從系統開(kāi)始時(shí)就(jiù)進(jìn)行規劃,每台服務器的編譯時(shí)間大約是 10 秒。
11 萬行代碼,對(duì)比流量來說(shuō)非常少。
使用這(zhè)種(zhǒng)極簡的方式主要基于幾個原因。首先,不需要太多測試,因爲 Meta.stackoverflow 本來就(jiù)是一個問題和 bug 讨論社區。其次,Meta.stackoverflow 還(hái)是一個軟件的測試網站,如果用戶發(fā)現問題的話,往往會(huì)提出并給予解決方案。
紐約數據中心使用的是 Windows 2012,已經(jīng)向(xiàng) 2012 R2 升級(Oregon 已經(jīng)完成(chéng)了升級),Linux 系統使用的是 Centos 6.4。
SSD
默認使用的是 Intel 330(Web 層等)
Intel 520 用于中間層寫入,比如 Elastic Search
數據層使用 Intel 710 和 S3700
系統同時(shí)使用了 RAID 1 和 RAID 10(任何4+ 以上的磁盤都(dōu)使用 RAID 10)。不畏懼故障發(fā)生,即使生産環境中使用了上千塊 2.5 英寸 SSD,還(hái)沒(méi)碰到過(guò)一塊失敗的情景。每個模型都(dōu)使用了 1 個以上的備件,多個磁盤發(fā)生故障的情景不在考慮之中。
ElasticSearch 在 SSD 上表現的異常出色,因爲 SO writes/re-indexes 的操作非常頻繁。
SSD 改變了搜索的使用方式。因爲鎖的問題,Luncene.net 并不能(néng)支撐 SO 的并發(fā)負載,因此他們轉向(xiàng)了 ElasticSearch。在全 SSD 環境下,并不需要圍繞 Binary Reader 建立鎖。
高可用性
異地備份——主數據中心位于紐約,備份數據中心在 Oregon。
Redis 有兩(liǎng)個從節點,SQL 有 2 個備份,Tag Engine 有 3 個節點,elastic 有 3 個節點,冗餘一切,并在兩(liǎng)個數據中心同時(shí)存在。
Nginx 是用于 SSL,終止 SSL 時(shí)轉換使用 HAProxy。
并不是主從所有,一些臨時(shí)的數據隻會(huì)放到緩存中
所有 HTTP 流量發(fā)送隻占總流量的 77%,還(hái)存在 Oregon 數據中心的備份及一些其他的 VPN 流量。這(zhè)些流量主要由 SQL 和 Redis 備份産生。
數據庫
MS SQL Server
Stack Exchange 爲每個網站都(dōu)設置了數據庫,因此 Stack Overflow 有一個、Server Fault 有一個,以此類推。
在紐約的主數據中心,每個集群通常都(dōu)使用 1 主和 1 隻讀備份的配置,同時(shí)還(hái)會(huì)在 Oregon 數據中心也設置一個備份。如果是運行的是 Oregon 集群,那麼(me)兩(liǎng)個在紐約數據中心的備份都(dōu)會(huì)是隻讀和同步的。
爲其他内容準備的數據庫。這(zhè)裡(lǐ)還(hái)存在一個“網絡範圍”的數據庫,用于儲存登陸憑證和聚合數據(大部分是 stackexchange.com 用戶文件或者 API)。
Careers Stack Overflow、stackexchange.com 和 Area 51 等都(dōu)擁有自己獨立的數據庫模式。
模式的變化需要同時(shí)提供給所有站點的數據庫,它們需要向(xiàng)下兼容,舉個例子,如果需要重命名一個列,那麼(me)將(jiāng)非常麻煩,這(zhè)裡(lǐ)需要進(jìn)行多個操作:增加一個新列,添加作用在兩(liǎng)個列上的代碼,給新列寫數據,改變代碼讓新列有效,移除舊列。
并不需要分片,所有事(shì)情通過(guò)索引來解決,而且數據體積也沒(méi)那麼(me)大。如果有 filtered indexes 需求,那麼(me)爲什麼(me)不更高效的進(jìn)行?常見模式隻在 DeletionDate = Null 上做索引,其他則通過(guò)爲枚舉指定類型。每項 votes 都(dōu)設置了 1 個表,比如一張表給 post votes,1 張表給 comment votes。大部分的頁面(miàn)都(dōu)可以實時(shí)渲染,隻爲匿名用戶緩存,因此,不存在緩存更新,隻有重查詢。
Scores 是非規範化的,因此需要經(jīng)常查詢。它隻包含 IDs 和 dates,post votes 表格當下大約有 56454478 行,使用索引,大部分的查詢都(dōu)可以在數毫秒内完成(chéng)。
Tag Engine 是完全獨立的,這(zhè)就(jiù)意味著(zhe)核心功能(néng)并不依賴任何外部應用程序。它是一個巨大的内存結構數組結構,專爲 SO 用例優化,并爲重負載組合進(jìn)行預計算。Tag Engine 是個簡單的 windows 服務,冗餘的運行在多個主機上。CPU 使用率基本上保持在2-5%,3 個主機專門用于冗餘,不負責任何負載。如果所有主機同時(shí)發(fā)生故障,網絡服務器將(jiāng)把 Tag Engine 加載到内存中持續運行。
關于 Dapper 無編譯器校驗查詢與傳統 ORM 的對(duì)比。使用編譯器有很多好(hǎo)處,但在運行時(shí)仍然會(huì)存在 fundamental disconnect 問題。同時(shí)更重要的是,由于生成(chéng) nasty SQL,通常情況還(hái)需要去尋找原始代碼,而 Query Hint 和 parameterization 控制等能(néng)力的缺乏更讓查詢優化變得複雜。
編碼
流程
大部分程序員都(dōu)是遠程工作,自己選擇編碼地點
編譯非常快
然後(hòu)運行少量的測試
一旦編譯成(chéng)功,代碼即轉移至開(kāi)發(fā)交付準備服務器
通過(guò)功能(néng)開(kāi)關隐藏新功能(néng)
在相同硬件上作爲其他站點測試運行
然後(hòu)轉移至 Meta.stackoverflow 測試,每天有上千個程序員在使用,一個很好(hǎo)的測試環境
如果通過(guò)則上線,在更廣大的社區進(jìn)行測試
大量使用靜态類和方法,爲了更簡單及更好(hǎo)的性能(néng)
編碼過(guò)程非常簡單,因爲複雜的部分被打包到庫裡(lǐ),這(zhè)些庫被開(kāi)源和維護。.Net 項目數量很低,因爲使用了社區共享的部分代碼。
開(kāi)發(fā)者同時(shí)使用 2 到 3 個顯示器,多個屏幕可以顯著提高生産效率。
緩存
緩存一切
5 個等級的緩存
1 級是網絡級緩存,緩存在浏覽器、CDN 以及代理服務器中。
2 級由 .Net 框架 HttpRuntime.Cache 完成(chéng),在每台服務器的内存中。
3 級 Redis,分布式内存鍵值存儲,在多個支撐同一個站點的服務器上共享緩存項。
4 級 SQL Server Cache,整個數據庫,所有數據都(dōu)被放到内存中。
5 級 SSD。通常隻在 SQL Server 預熱後(hòu)才生效。
舉個例子,每個幫助頁面(miàn)都(dōu)進(jìn)行了緩存,訪問一個頁面(miàn)的代碼非常簡單:
使用了靜态的方法和類。從 OOP 角度來看确實很糟,但是非常快并有利于簡潔編碼。
緩存由 Redis 和 Dapper 支撐,一個微型 ORM
爲了解決垃圾收集問題,模闆中 1 個類隻使用 1 個副本,被建立和保存在緩存中。監測一切,包括 GC 操。據統計顯示,間接層增加 GC 壓力達到了某個程度時(shí)會(huì)顯著的降低性能(néng)。
CDN Hit 。鑒于查詢字符串基于文件内容進(jìn)行哈希,隻在有新建立時(shí)才會(huì)被再次取出。每天 3000 萬到 5000 萬 Hit,帶寬大約爲 300GB 到 600GB。
CDN 不是用來應對(duì) CPU 或I/O負載,而是幫助用戶更快的獲得答案
部署
每天 5 次部署,不去建立過(guò)大的應用。主要因爲
可以直接的監視性能(néng)
盡可能(néng)最小化建立,可以工作才是重點
産品建立後(hòu)再通過(guò)強大的腳本拷貝到各個網頁層,每個服務器的步驟是:
通過(guò) POST 通知 HAProxy 下架某台服務器
延遲 IIS 結束現有請求(大約 5 秒)
停止網站(通過(guò)同一個 PSSession 結束所有下遊)
Robocopy 文件
開(kāi)啓網站
通過(guò)另一個 POST 做 HAProxy Re-enable
幾乎所有部署都(dōu)是通過(guò) puppet 或 DSC,升級通常隻是大幅度調整 RAID 陣列并通過(guò) PXE boot 安裝,這(zhè)樣做非常快速。
協作
團隊
SRE (System Reliability Engineering):5 人
Core Dev(Q&A site)6-7 人
Core Dev Mobile:6 人
Careers 團隊專門負責 SO Careers 産品開(kāi)發(fā):7 人
Devops 和開(kāi)發(fā)者結合的非常緊密
團隊間變化很大
大部分員工遠程工作
辦公室主要用于銷售,Denver 和 London 除外
一切平等,些許偏向(xiàng)紐約工作者,因爲面(miàn)對(duì)面(miàn)有助于工作交流,但是在線工作影響也并不大
對(duì)比可以在同一個辦公室辦公,他們更偏向(xiàng)熱愛産品及有才華的工程師,他們可以很好(hǎo)的衡量利弊
許多人因爲家庭而選擇遠程工作,紐約是不錯,但是生活并不寬松
辦公室設立在曼哈頓,那是個人才的誕生地。數據中心不能(néng)太偏,因爲經(jīng)常會(huì)涉及升級
打造一個強大團隊,偏愛極客。早期的微軟就(jiù)聚集了大量極客,因此他們征服了整個世界
Stack Overflow 社區也是個招聘的地點,他們在那尋找熱愛編碼、樂于助人及熱愛交流的人才。
編制預算
預算是項目的基礎。錢隻花在爲新項目建立基礎設施上,如此低利用率的 web server 還(hái)是 3 年前數據中心建立時(shí)購入。
測試
快速叠代和遺棄
許多測試都(dōu)是發(fā)布隊伍完成(chéng)的。開(kāi)發(fā)擁有一個同樣的 SQL 服務器,并且運行在相同的 Web 層,因此性能(néng)測試并不會(huì)糟糕。
非常少的測試。Stack Overflow 并沒(méi)有進(jìn)行太多的單元測試,因爲他們使用了大量的靜态代碼,還(hái)有一個非常活躍的社區。
基礎設施改變。鑒于所有東西都(dōu)有雙份,所以每個舊配置都(dōu)有備份,并使用了一個快速故障恢複機制。比如,keepalived 可以在負載均衡器中快速回退。
對(duì)比定期維護,他們更願意依賴冗餘系統。SQL 備份用一個專門的服務器進(jìn)行測試,隻爲了可以重存儲。計劃做每兩(liǎng)個月一次的全數據中心故障恢複,或者使用完全隻讀的第二數據中心。
每次新功能(néng)發(fā)布都(dōu)做單元測試、集成(chéng)測試盒 UI 測試,這(zhè)就(jiù)意味著(zhe)可以預知輸入的産品功能(néng)測試後(hòu)就(jiù)會(huì)推送到孵化網站,即 meta.stackexchange(原 meta.stackoverflow)。
監視/日志
當下正在考慮使用 http://logstash.net/做日志管理,目前使用了一個專門的服務將(jiāng) syslog UDP 傳輸到 SQL 數據庫中。網頁中爲計時(shí)添加 header,這(zhè)樣就(jiù)可以通過(guò) HAProxy 來捕獲并且融合到 syslog 傳輸中。
Opserver 和 Realog 用于顯示測量結果。Realog 是一個日志展示系統,由 Kyle Brandt 和 Matt Jibson 使用 Go 建立。
日志通過(guò) HAProxy 負載均衡器借助 syslog 完成(chéng),而不是 IIS,因爲其功能(néng)比 IIS 更豐富。
關于雲
還(hái)是老生常談,硬件永遠比開(kāi)發(fā)者和有效率的代碼便宜。基于木桶效應,速度肯定受限于某個短闆,現有的雲服務基本上都(dōu)存在容量和性能(néng)限制。
如果從開(kāi)始就(jiù)使用雲來建設 SO 說(shuō)不定也會(huì)達到現在的水準。但毫無疑問的是,如果達到同樣的性能(néng),使用雲的成(chéng)本將(jiāng)遠遠高于自建數據中心。
性能(néng)至上
StackOverflow 是個重度的性能(néng)控,主頁加載的時(shí)間永遠控制在 50 毫秒内,當下的響應時(shí)間是 28 毫秒。
程序員熱衷于降低頁面(miàn)加載時(shí)間以及提高用戶體驗。
每個獨立的網絡提交都(dōu)予以計時(shí)和記錄,這(zhè)種(zhǒng)計量可以弄清楚提升性能(néng)需要修改的地方。
如此低資源利用率的主要原因就(jiù)是高效的代碼。web server 的 CPU 平均利用率在5% 到 15% 之間,内存使用爲 15.5 GB,網絡傳輸在 20 Mb/s到 40 Mb/s。SQL 服務器的 CPU 使用率在5% 到 10% 之間,内存使用是 365GB,網絡傳輸爲 100 Mb/s到 200 Mb/s。這(zhè)可以帶來 3 個好(hǎo)處:給升級留下很大的空間;在嚴重錯誤發(fā)生時(shí)可以保持服務可用;在需要時(shí)可以快速回檔。
學(xué)到的知識
1. 爲什麼(me)使用 MS 産品的同時(shí)還(hái)使用 Redis?什麼(me)好(hǎo)用用什麼(me),不要做無必要的系統之争,比如 C# 在 Windows 機器上運行最好(hǎo),我們使用 IIS;Redis 在*nix 機器上可以得到充分發(fā)揮,我們使用*nix。
2. Overkill 即策略。平常的利用率并不能(néng)代表什麼(me),當某些特定的事(shì)情發(fā)生時(shí),比如備份、重建等完全可以將(jiāng)資源使用拉滿。
3. 堅固的 SSD。所有數據庫都(dōu)建立在 SSD 之上,這(zhè)樣可以獲得 0 延時(shí)。
4. 了解你的讀寫負載。
5. 高效的代碼意味著(zhe)更少的主機。隻有新項目上線時(shí)才會(huì)因爲特殊需求增加硬件,通常情況下是添加内存,但在此之外,高效的代碼就(jiù)意味著(zhe) 0 硬件添加。所以經(jīng)常隻讨論兩(liǎng)個問題:爲存儲增加新的 SSD;爲新項目增加硬件。
6. 不要害怕定制化。SO 在 Tag 上使用複雜查詢,因此專門開(kāi)發(fā)了所需的 Tag Engine。
7. 隻做必須做的事(shì)情。之所以不需要測試是因爲有一個活躍的社區支撐,比如,開(kāi)發(fā)者不用擔心出現“Square Wheel”效應,如果開(kāi)發(fā)者可以制作一個更更輕量級的組件,那就(jiù)替代吧。
8. 注重硬件知識,比如 IL。一些代碼使用 IL 而不是C#。聚焦 SQL 查詢計劃。使用 web server 的内存轉儲究竟做了些什麼(me)。探索,比如爲什麼(me)一個 split 會(huì)産生 2GB 的垃圾。
9. 切勿官僚作風。總有一些新的工具是你需要的,比如,一個編輯器,新版本的 Visual Studio,降低提升過(guò)程中的一切阻力。
10. 垃圾回收驅動編程。SO 在減少垃圾回收成(chéng)本上做了很多努力,跳過(guò)類似 TDD 的實踐,避免抽象層,使用靜态方法。雖然極端,但是确實打造出非常高效的代碼。
11. 高效代碼的價值遠遠超出你想象,它可以讓硬件跑的更快,降低資源使用,切記讓代碼更容易被程序員理解。