了解知乎網(wǎng)站的架構(gòu)演進(jìn)史
初期架構(gòu)選型
在2010年10月真正開始動(dòng)手做知乎這個(gè)產(chǎn)品時(shí),包含李申申在內(nèi),最初只有兩位工程師;到2010年12月份上線時(shí),工程師是四個(gè)。
知乎的主力開發(fā)語(yǔ)言是Python。因?yàn)镻ython簡(jiǎn)單且強(qiáng)大,能夠快速上手,開發(fā)效率高,而且社區(qū)活躍,團(tuán)隊(duì)成員也比較喜歡。
知乎使用的是Tornado框架。因?yàn)樗С之惒?,很適合做實(shí)時(shí)comet應(yīng)用,而且簡(jiǎn)單輕量,學(xué)習(xí)成本低,再就是有FriendFeed 的成熟案例,F(xiàn)acebook 的社區(qū)支持。知乎的產(chǎn)品有個(gè)特性,就是希望跟瀏覽器端建立一個(gè)長(zhǎng)連接,便于實(shí)時(shí)推送Feed和通知,所以Tornado比較合適。
最初整個(gè)團(tuán)隊(duì)的精力全部放在產(chǎn)品功能的開發(fā)上,而其他方面,基本上能節(jié)約時(shí)間、能省的都用最簡(jiǎn)單的方法來(lái)解決,當(dāng)然這在后期也帶來(lái)了一些問(wèn)題。
最初的想法是用云主機(jī),節(jié)省成本。知乎的第一臺(tái)服務(wù)器是512MB內(nèi)存的Linode主機(jī)。但是網(wǎng)站上線后,內(nèi)測(cè)受歡迎程度超出預(yù)期,很多用戶反饋網(wǎng)站很慢??鐕?guó)網(wǎng)絡(luò)延遲比想象的要大,特別是國(guó)內(nèi)的網(wǎng)絡(luò)不均衡,全國(guó)各地用戶訪問(wèn)的情況都不太一樣。這個(gè)問(wèn)題,再加上當(dāng)時(shí)要做域名備案,知乎又回到了自己買機(jī)器找機(jī)房的老路上。
買了機(jī)器、找了機(jī)房之后又遇到了新的問(wèn)題,服務(wù)經(jīng)常宕掉。當(dāng)時(shí)服務(wù)商的機(jī)器內(nèi)存總是出問(wèn)題,動(dòng)不動(dòng)就重啟。終于有一次機(jī)器宕掉起不來(lái)了,這時(shí)知乎就做了Web和數(shù)據(jù)庫(kù)的高可用。創(chuàng)業(yè)就是這樣一個(gè)情況,永遠(yuǎn)不知道明早醒來(lái)的時(shí)候會(huì)面臨什么樣的問(wèn)題。
這是當(dāng)時(shí)那個(gè)階段的架構(gòu)圖,Web和數(shù)據(jù)庫(kù)都做了主從。當(dāng)時(shí)的圖片服務(wù)托管在又拍云上。 除了主從,為了性能更好還做了讀寫分離。為解決同步問(wèn)題,又添加了一個(gè)服務(wù)器來(lái)跑離線腳本,避免對(duì)線上服務(wù)造成響應(yīng)延遲。另外,為改進(jìn)內(nèi)網(wǎng)的吞吐量延遲, 還更換了設(shè)備,使整個(gè)內(nèi)網(wǎng)的吞吐量翻了20倍。
在2011年上半年時(shí),知乎對(duì)Redis已經(jīng)很依賴。除了最開始的隊(duì)列、搜索在用,后來(lái)像Cache也開始使用,單機(jī)存儲(chǔ)成為瓶頸,所以引入了分片,同時(shí)做了一致性。
知乎團(tuán)隊(duì)是一個(gè)很相信工具的團(tuán)隊(duì),相信工具可以提升效率。工具其實(shí)是一個(gè)過(guò)程,工具并沒(méi)有所謂的最好的工具,只有最適合的工具。而且它是在整個(gè)過(guò)程中,隨著整個(gè)狀態(tài)的變化、環(huán)境的變化在不斷發(fā)生變化的。知乎自己開發(fā)或使用過(guò)的工具包括Profiling(函數(shù)級(jí)追蹤請(qǐng)求,分析調(diào)優(yōu))、Werkzeug(方便調(diào)試的工具)、Puppet(配置管理)和Shipit(一鍵上線或回滾)等。
日志系統(tǒng)
知乎最初是邀請(qǐng)制的,2011年下半年,知乎上線了申請(qǐng)注冊(cè),沒(méi)有邀請(qǐng)碼的用戶也可以通過(guò)填寫一些資料申請(qǐng)注冊(cè)知乎。用戶量又上了一個(gè)臺(tái)階,這時(shí)就有了一些發(fā)廣告的賬戶,需要掃除廣告。日志系統(tǒng)的需求提上日程。
這個(gè)日志系統(tǒng)必須支持分布式收集、集中存儲(chǔ)、實(shí)時(shí)、可訂閱和簡(jiǎn)單等特性。當(dāng)時(shí)調(diào)研了一些開源系統(tǒng),比如Scribe總體不錯(cuò),但是不支持訂閱。Kafka是Scala開發(fā)的,但是團(tuán)隊(duì)在Scala方面積累較少,F(xiàn)lume也是類似,而且比較重。所以開發(fā)團(tuán)隊(duì)選擇了自己開發(fā)一個(gè)日志系統(tǒng)——Kids(Kids Is Data Stream)。顧名思義,Kids是用來(lái)匯集各種數(shù)據(jù)流的。
Kids參考了Scribe的思路。Kdis在每臺(tái)服務(wù)器上可以配置成Agent或 Server。Agent直接接受來(lái)自應(yīng)用的消息,把消息匯集之后,可以打給下一個(gè)Agent或者直接打給中心Server。訂閱日志時(shí),可以從 Server上獲取,也可以從中心節(jié)點(diǎn)的一些Agent上獲取。
具體細(xì)節(jié)如下圖所示:
知乎還基于Kids做了一個(gè)Web小工具(Kids Explorer),支持實(shí)時(shí)看線上日志,現(xiàn)在已經(jīng)成為調(diào)試線上問(wèn)題最主要的工具。
Kids已經(jīng)開源,放到了Github上。
事件驅(qū)動(dòng)的架構(gòu)
知乎這個(gè)產(chǎn)品有一個(gè)特點(diǎn),最早在添加一個(gè)答案后,后續(xù)的操作其實(shí)只有更新通知、更新動(dòng) 態(tài)。但是隨著整個(gè)功能的增加,又多出了一些更新索引、更新計(jì)數(shù)、內(nèi)容審查等操作,后續(xù)操作五花八門。如果按照傳統(tǒng)方式,維護(hù)邏輯會(huì)越來(lái)越龐大,維護(hù)性也會(huì) 非常差。這種場(chǎng)景很適合事件驅(qū)動(dòng)方式,所以開發(fā)團(tuán)隊(duì)對(duì)整個(gè)架構(gòu)做了調(diào)整,做了事件驅(qū)動(dòng)的架構(gòu)。
這時(shí)首先需要的是一個(gè)消息隊(duì)列,它應(yīng)該可以獲取到各種各樣的事件,而且對(duì)一致性有很高的 要求。針對(duì)這個(gè)需求,知乎開發(fā)了一個(gè)叫Sink的小工具。它拿到消息后,先做本地的保存、持久化,然后再把消息分發(fā)出去。如果那臺(tái)機(jī)器掛掉了,重啟時(shí)可以 完整恢復(fù),確保消息不會(huì)丟失。然后它通過(guò)Miller開發(fā)框架,把消息放到任務(wù)隊(duì)列。Sink更像是串行消息訂閱服務(wù),但任務(wù)需要并行化處理, Beanstalkd就派上了用場(chǎng),由其對(duì)任務(wù)進(jìn)行全周期管理。架構(gòu)如下圖所示:
舉例而言,如果現(xiàn)在有用戶回答了問(wèn)題,首先系統(tǒng)會(huì)把問(wèn)題寫到MySQL里面,把消息塞到Sink,然后把問(wèn)題返回給用戶。Sink通過(guò)Miller把任務(wù)發(fā)給 Beanstalkd,Worker自己可以找到任務(wù)并處理。
最開始上線時(shí),每秒鐘有10個(gè)消息,然后有70個(gè)任務(wù)產(chǎn)生。現(xiàn)在每秒鐘有100個(gè)事件,有1500個(gè)任務(wù)產(chǎn)生,就是通過(guò)現(xiàn)在的事件驅(qū)動(dòng)架構(gòu)支撐的。
頁(yè)面渲染優(yōu)化
知乎在2013年時(shí)每天有上百萬(wàn)的PV,頁(yè)面渲染其實(shí)是計(jì)算密集型的,另外因?yàn)橐@取數(shù)據(jù),所以也有IO密集型的特點(diǎn)。這時(shí)開發(fā)團(tuán)隊(duì)就對(duì)頁(yè)面進(jìn)行了組件化,還升級(jí)了數(shù)據(jù)獲取機(jī)制。知乎按照整個(gè)頁(yè)面組件樹的結(jié)構(gòu),自上而下分層地獲取數(shù)據(jù),當(dāng)上 層的數(shù)據(jù)已經(jīng)獲取了,下層的數(shù)據(jù)就不需要再下去了,有幾層基本上就有幾次數(shù)據(jù)獲取。
結(jié)合這個(gè)思路,知乎自己做了一套模板渲染開發(fā)框架——ZhihuNode。
經(jīng)歷了一系列改進(jìn)之后,頁(yè)面的性能大幅度提升。問(wèn)題頁(yè)面從500ms 減少到150ms,F(xiàn)eed頁(yè)面從1s減少到600ms。
面向服務(wù)的架構(gòu)(SOA)
隨著知乎的功能越來(lái)越龐雜,整個(gè)系統(tǒng)也越來(lái)越大。知乎是怎么做的服務(wù)化呢?
首先需要一個(gè)最基本的RPC框架,RPC框架也經(jīng)歷了好幾版演進(jìn)。
第一版是Wish,它是一個(gè)嚴(yán)格定義序列化的模型。傳輸層用到了STP,這是自己寫的很 簡(jiǎn)單的傳輸協(xié)議,跑在TCP上。一開始用的還不錯(cuò),因?yàn)橐婚_始只寫了一兩個(gè)服務(wù)。但是隨著服務(wù)增多,一些問(wèn)題開始出現(xiàn),首先是 ProtocolBuffer會(huì) 生成一些描述代碼,很冗長(zhǎng),放到整個(gè)庫(kù)里顯得很丑陋。另外嚴(yán)格的定義使其不便使用。這時(shí)有位工程師開發(fā)了新的RPC框架——Snow。它使用簡(jiǎn)單的 JSON做數(shù)據(jù)序列化。但是松散的數(shù)據(jù)定義面對(duì)的問(wèn)題是,比如說(shuō)服務(wù)要去升級(jí),要改寫數(shù)據(jù)結(jié)構(gòu),很難知道有哪幾個(gè)服務(wù)在使用,也很難通知它們,往往錯(cuò)誤就 發(fā)生了。于是又出了第三個(gè)RPC框架,寫RPC框架的工程師,希望結(jié)合前面兩個(gè)框架的特點(diǎn),首先保持Snow簡(jiǎn)單,其次需要相對(duì)嚴(yán)格的序列化協(xié)議。這一版 本引入了 Apache Avro。同時(shí)加入了特別的機(jī)制,在傳輸層和序列化協(xié)議這一層都做成了可插拔的方式,既可以用JSON,也可以用Avro,傳輸層可以用STP,也可以用 二進(jìn)制協(xié)議。
再就是搭了一個(gè)服務(wù)注冊(cè)發(fā)現(xiàn),只需要簡(jiǎn)單的定義服務(wù)的名字就可以找到服務(wù)在哪臺(tái)機(jī)器上。同時(shí),知乎也有相應(yīng)的調(diào)優(yōu)的工具,基于Zipkin開發(fā)了自己的 Tracing系統(tǒng)。
按照調(diào)用關(guān)系,知乎的服務(wù)分成了3層:聚合層、內(nèi)容層和基礎(chǔ)層。按屬性又可以分成3類:數(shù)據(jù)服務(wù)、邏輯服務(wù)和通道服務(wù)。數(shù)據(jù)服務(wù)主要是一些要做特殊數(shù)據(jù)類型的存儲(chǔ),比如圖片服務(wù)。邏輯服務(wù)更多的是CPU密集、計(jì)算密集的操作,比如答案格式的定義、解析等。通道服務(wù)的特點(diǎn)是沒(méi)有存儲(chǔ),更多是做一個(gè)轉(zhuǎn)發(fā),比如說(shuō)Sink。
這是引入服務(wù)化之后整體的架構(gòu)。
而目前在產(chǎn)品方面,知乎保留著以下幾個(gè)重點(diǎn):
1.基礎(chǔ)模塊(1 問(wèn)題-n 回答-n 評(píng)論模塊)
知乎基礎(chǔ)模塊中一個(gè)問(wèn)題對(duì)應(yīng)于 n 個(gè)回答,一個(gè)回答又對(duì)應(yīng)于 n 個(gè)評(píng)論,因此我們可以把基礎(chǔ)模塊稱為 1 問(wèn)題-n 回答-n 評(píng)論模塊。假設(shè)知乎架構(gòu)模型中僅存在基礎(chǔ)模塊,將會(huì)是一個(gè)怎樣的場(chǎng)景?那就是信息流隨著時(shí)間的推移不斷生成新的內(nèi)容并把舊信息快速替換沖刷掉,這種對(duì)基礎(chǔ)模塊無(wú)差別的線性陳列,對(duì)用戶來(lái)說(shuō)將是一個(gè)災(zāi)難:
在簡(jiǎn)單羅列的線性信息海洋中,用戶汲取其所需信息的成本太高;
信息流如同大河奔流,那些有挖掘價(jià)值的信息點(diǎn)稍縱即逝,即信息價(jià)值被嚴(yán)重?fù)]霍;
用戶不能將有價(jià)值的信息點(diǎn)從信息大河里“舀”出來(lái),信息可見(jiàn)而不可用,無(wú)法產(chǎn)生長(zhǎng)效作用。
知乎的產(chǎn)品設(shè)計(jì)者很好地意識(shí)到了這些潛在的“災(zāi)難”,并對(duì)每個(gè)問(wèn)題點(diǎn)做出了針對(duì)性的產(chǎn)品設(shè)計(jì)方案,下面木柄逐一展開分析。
2.話題模塊
話題模塊用來(lái)解決“在線性簡(jiǎn)單羅列的信息海洋中,用戶汲取所需信息的成本太高”的問(wèn)題。知乎中,每一個(gè)基礎(chǔ)模塊(1 問(wèn)題-n 回答-n 評(píng)論模塊)可以添加“話題”標(biāo)識(shí),“話題”描述了基礎(chǔ)模塊的“類別”,話題模塊與基礎(chǔ)模塊是多對(duì)多的映射關(guān)系(many2many)。事實(shí)上,為內(nèi)容添加“標(biāo)識(shí)”的做法在以內(nèi)容為核心的網(wǎng)站的組織架構(gòu)模型中屢見(jiàn)不鮮,很多網(wǎng)站將這種“標(biāo)識(shí)”稱為標(biāo)簽(比如 lofter)。
但是知乎的話題比普通網(wǎng)站的標(biāo)簽走的更遠(yuǎn):知乎的各個(gè)話題之間不像標(biāo)簽?zāi)菢邮枪铝⒌模x了一套將話題組織起來(lái)的數(shù)據(jù)結(jié)構(gòu)。請(qǐng)注意,話題本身就是對(duì)基礎(chǔ)模塊的一種組織形式,而又存在一套數(shù)據(jù)結(jié)構(gòu)描述了話題的組織形式,那么我們可以將這種數(shù)據(jù)結(jié)構(gòu)稱作“描述結(jié)構(gòu)組織的結(jié)構(gòu)組織”,知乎自己是這么介紹這個(gè)“描述結(jié)構(gòu)組織的結(jié)構(gòu)組織”:知乎的全部話題通過(guò)父子關(guān)系構(gòu)成一個(gè)有根無(wú)循環(huán)的有向圖;根話題即為所有話題的最上層的父話題;請(qǐng)不要在問(wèn)題上直接綁定根話題。
3.發(fā)現(xiàn)模塊
發(fā)現(xiàn)模塊解決了信息流如同大河奔流,那些有挖掘價(jià)值的信息點(diǎn)稍縱即逝,即信息價(jià)值被嚴(yán)重?fù)]霍的問(wèn)題。發(fā)現(xiàn)模塊主要有兩部分內(nèi)容組成:推薦與熱門。熱門內(nèi)容是由用戶群體行為所做出來(lái)的“內(nèi)容精選”,而推薦內(nèi)容是知乎運(yùn)營(yíng)人員對(duì)“群體行為”的補(bǔ)充完善,最大程度地讓有價(jià)值的信息減緩流速,或者二次“逆流”,目的就是讓有價(jià)值的信息得以“上浮”與“駐留”。
此外值得一提的是,如果說(shuō)發(fā)現(xiàn)模塊是構(gòu)筑在知乎基礎(chǔ)模塊上的信息“駐留模塊”, 那么話題模塊也有一個(gè)針對(duì)其信息的“駐留模塊”——“話題精華模塊”。發(fā)現(xiàn)模塊是挖掘全局的有價(jià)值的信息,而“話題精華模塊”挖掘的是該話題的有價(jià)值的信息,從而使有價(jià)值的信息在不同的組織維度上得到“駐留”,而不被浩大的信息流沖的無(wú)影無(wú)蹤。
4.收藏模塊
收藏模塊解決了用戶不能將有價(jià)值的信息點(diǎn)從信息大河里“舀”出來(lái),信息可見(jiàn)而不可用,無(wú)法產(chǎn)生長(zhǎng)效作用的問(wèn)題。收藏功能是很多內(nèi)容為王的網(wǎng)站架構(gòu)中重要的一環(huán),使用戶可以從浩淼的信息流中舀出其感興趣的那一瓢。知乎的收藏模塊支持創(chuàng)建收藏文件夾,即用戶可以對(duì)收藏內(nèi)容再組織,存放到相應(yīng)的收藏文件夾中。
此外知乎的收藏模塊還走的更遠(yuǎn),用戶組織的收藏夾可以設(shè)置為“公有”狀態(tài),并分享給其他用戶。也就是用戶的利己行為(收藏自己感興趣或者有幫助的內(nèi)容),產(chǎn)生了利他的效果(其他用戶也能看到由別人的收藏夾并從中獲益)。從內(nèi)容組織角度上來(lái)說(shuō),知乎的收藏夾不但提供了將信息“舀”出保存的作用,而且也起到將優(yōu)質(zhì)信息“駐留”與“上浮”的作用。
5.知乎日?qǐng)?bào)模塊
知乎日?qǐng)?bào)模塊是一個(gè)比較特殊的模塊,它并不是知乎的主體模塊,你可以將其理解成知乎產(chǎn)品的衍生模塊,它事實(shí)上也從另外一個(gè)角度在解答信息價(jià)值被嚴(yán)重?fù)]霍的問(wèn)題。知乎日?qǐng)?bào)模塊與知乎主體模塊采用松耦合的架構(gòu)模式,它是對(duì)知乎這個(gè)龐大的優(yōu)質(zhì)內(nèi)容生產(chǎn)機(jī)器的二次開發(fā)。知乎日?qǐng)?bào)采取“日?qǐng)?bào)”的方式,每天對(duì)知乎中產(chǎn)生的經(jīng)典內(nèi)容做一次組織成刊。知乎日?qǐng)?bào)簡(jiǎn)單的布局、呈現(xiàn)方式,更加符合人們?cè)谝苿?dòng)端的閱讀習(xí)慣,使那些覺(jué)得在移動(dòng)端使用知乎不方便的用戶,或者想在碎片時(shí)間里進(jìn)行閱讀的用戶,有一個(gè)更加貼心的知乎產(chǎn)品可以選擇。
版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。