核心內(nèi)容:58同城流量從小到大過(guò)程中,架構(gòu)是如何演進(jìn)的?遇到了哪些問(wèn)題?以及如何解決這些問(wèn)題?
核心觀點(diǎn):好的架構(gòu)不是設(shè)計(jì)出來(lái)的,而是進(jìn)化而來(lái)的。
如何演進(jìn):站點(diǎn)流量在不同階段,會(huì)遇到不同的問(wèn)題,找到對(duì)應(yīng)階段站點(diǎn)架構(gòu)所面臨的主要問(wèn)題,在不斷解決這些問(wèn)題的過(guò)程中,整個(gè)系統(tǒng)的架構(gòu)就不斷的演進(jìn)了。
如何演進(jìn),簡(jiǎn)言之:找到主要矛盾,并解決主要矛盾。
建站之初,站點(diǎn)流量非常小,可能低于十萬(wàn)級(jí)別。這意味著,平均每秒鐘也就幾次訪問(wèn)。請(qǐng)求量比較低,數(shù)據(jù)量比較小,代碼量也比較小,幾個(gè)工程師,很短的時(shí)間搭起這樣的系統(tǒng),甚至沒(méi)有考慮“架構(gòu)”的問(wèn)題。
這是一個(gè)單機(jī)系統(tǒng),所有的站點(diǎn)、數(shù)據(jù)庫(kù)、文件都部署在一臺(tái)服務(wù)器上。工程師每天的核心工作是CURD,瀏覽器端傳過(guò)來(lái)一些數(shù)據(jù),解析GET/POST/COOKIE中傳過(guò)來(lái)的數(shù)據(jù),拼裝成一些CURD的sql語(yǔ)句訪問(wèn)數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)返回?cái)?shù)據(jù),拼裝成頁(yè)面,返回瀏覽器。相信很多創(chuàng)業(yè)團(tuán)隊(duì)的工程師,初期做的也是類(lèi)似的工作。
58同城最初選擇的是微軟技術(shù)體系這條路:Windows、iis、SQL-Sever、C#
如果重新再來(lái),我們可能會(huì)選擇LAMP體系。
初創(chuàng)階段,工程師面臨的主要問(wèn)題:寫(xiě)CURD的sql語(yǔ)句很容易出錯(cuò)。
我們?cè)谶@個(gè)階段引進(jìn)DAO和ORM,讓工程師們不再直接面對(duì)CURD的sql語(yǔ)句,而是面對(duì)他們比較擅長(zhǎng)的面向?qū)ο箝_(kāi)發(fā),極大的提高了編碼效率,降低了出錯(cuò)率。
隨著流量越來(lái)越大,老板不只要求“有一個(gè)可以看見(jiàn)的站點(diǎn)”,他希望網(wǎng)站能夠正常訪問(wèn),當(dāng)然速度快點(diǎn)就更好了。
而此時(shí)系統(tǒng)面臨問(wèn)題是:流量的高峰期容易宕機(jī),大量的請(qǐng)求會(huì)壓到數(shù)據(jù)庫(kù)上,數(shù)據(jù)庫(kù)成為新的瓶頸,人多并行訪問(wèn)時(shí)站點(diǎn)非??ā_@時(shí),我們的機(jī)器數(shù)量也從一臺(tái)變成了多臺(tái),我們的系統(tǒng)成了所謂的(偽)“分布式架構(gòu)”:
我們使用了一些常見(jiàn)優(yōu)化手段:
(1)動(dòng)靜分離,動(dòng)態(tài)的頁(yè)面通過(guò)Web-Server訪問(wèn),靜態(tài)的文件例如圖片就放到單獨(dú)的文件服務(wù)器上;
(2)讀寫(xiě)分離,將落到數(shù)據(jù)庫(kù)上的讀寫(xiě)請(qǐng)求分派到不同的數(shù)據(jù)庫(kù)服務(wù)器上;
互聯(lián)網(wǎng)絕大部分的業(yè)務(wù)場(chǎng)景,都是讀多寫(xiě)少。對(duì)58同城來(lái)說(shuō),絕大部分用戶(hù)的需求是訪問(wèn)信息,搜索信息,只有少數(shù)的用戶(hù)發(fā)貼。此時(shí)讀取性能容易成為瓶頸,那么如何擴(kuò)展整個(gè)站點(diǎn)架構(gòu)的讀性能呢?常用的方法是主從同步,增加從庫(kù)。我們?cè)瓉?lái)只有一個(gè)讀數(shù)據(jù)庫(kù),現(xiàn)在有多個(gè)讀數(shù)據(jù)庫(kù),就提高了讀性能。
要解決耦合的問(wèn)題,最先想到的是針對(duì)核心業(yè)務(wù)做切分,工程師根據(jù)業(yè)務(wù)切分對(duì)系統(tǒng)也進(jìn)行切分:我們將業(yè)務(wù)垂直拆分成了首頁(yè)、發(fā)布頁(yè)、列表頁(yè)和詳情頁(yè)。
另外,我們?cè)?span style="color:#e33737;">數(shù)據(jù)庫(kù)層面也進(jìn)行了垂直拆分,將單庫(kù)數(shù)據(jù)量降下來(lái),讓讀寫(xiě)延時(shí)得到緩解。
同時(shí),還使用了這些技術(shù)來(lái)優(yōu)化系統(tǒng)和提高研發(fā)效率:
(1)對(duì)動(dòng)態(tài)資源和靜態(tài)資源進(jìn)行拆分。對(duì)靜態(tài)資源我們使用了CDN服務(wù),用戶(hù)就近訪問(wèn),靜態(tài)資源的訪問(wèn)速度得到很明顯的提升;
(2)除此之外,我們還使用了MVC模式,擅長(zhǎng)前端的工程師去做展示層,擅長(zhǎng)業(yè)務(wù)邏輯的工程師就做控制層,擅長(zhǎng)數(shù)據(jù)的工程師就做數(shù)據(jù)層,專(zhuān)人專(zhuān)用,研發(fā)效率和質(zhì)量又進(jìn)一步提高。
流量越來(lái)越大,當(dāng)流量達(dá)到百萬(wàn)甚至千萬(wàn)時(shí),站點(diǎn)面臨一個(gè)很大的問(wèn)題就是性能和成本的折衷。上文提到58同城最初的技術(shù)選型是Windows,我們?cè)谶@個(gè)階段做了一次脫胎換骨的技術(shù)轉(zhuǎn)型,全面轉(zhuǎn)向開(kāi)源技術(shù):
(1)操作系統(tǒng)轉(zhuǎn)型Linux
(2)數(shù)據(jù)庫(kù)轉(zhuǎn)型Mysql
(3)web服務(wù)器轉(zhuǎn)型Tomcat
(4)開(kāi)發(fā)語(yǔ)言轉(zhuǎn)向了Java
其實(shí),很多互聯(lián)網(wǎng)公司在流量從小到大的過(guò)程中都經(jīng)歷過(guò)類(lèi)似的轉(zhuǎn)型,例如京東和淘寶。
除此之外,為了保證站點(diǎn)的高可用,我們使用了反向代理。
什么是代理?代理就是代表用戶(hù)訪問(wèn)xxoo站點(diǎn)。
什么是反向代理?反向代理代表的是58網(wǎng)站,用戶(hù)不用關(guān)注訪問(wèn)是58同城的哪臺(tái)服務(wù)器,由反向代理來(lái)代表58同城。58同城通過(guò)反向代理,DNS輪詢(xún), LVS等技術(shù),來(lái)保證接入層的高可用性。
另外,為了保證服務(wù)層和數(shù)據(jù)層的高可用,我們采用了冗余的方法,單點(diǎn)服務(wù)不可用,我們就冗余服務(wù),單點(diǎn)數(shù)據(jù)不可用,我們就冗余數(shù)據(jù)。
這個(gè)階段58同城進(jìn)入了一個(gè)業(yè)務(wù)高速爆發(fā)期,短期內(nèi)衍生出非常多的業(yè)務(wù)站點(diǎn)和服務(wù)。新增站點(diǎn)、新增服務(wù)每次都會(huì)做一些重復(fù)的事情,例如線程模型,消息隊(duì)列,參數(shù)解析等等,于是,58同城就研發(fā)了自己的站點(diǎn)框架和服務(wù)框架,現(xiàn)在這兩個(gè)框架也都已經(jīng)開(kāi)源:
(1)站點(diǎn)框架Argo:https://github.com/58code/Argo
(2)服務(wù)框架Gaea:https://github.com/58code/Gaea
現(xiàn)在,58同城的流量已經(jīng)達(dá)到10億的量級(jí),架構(gòu)上我們規(guī)劃做一些什么樣的事情呢,幾個(gè)方向:
(1)業(yè)務(wù)服務(wù)化
(2)多架構(gòu)模式
(3)平臺(tái)化
(4)...
最后做一個(gè)簡(jiǎn)單的總結(jié),網(wǎng)站在不同的階段遇到的問(wèn)題不一樣,而解決這些問(wèn)題使用的技術(shù)也不一樣:
(1)流量小的時(shí)候,我們要提高開(kāi)發(fā)效率,可以在早期要引入ORM,DAO;
(2)流量變大,可以使用動(dòng)靜分離、讀寫(xiě)分離、主從同步、垂直拆分、CDN、MVC等方式不斷提升網(wǎng)站的性能和研發(fā)效率;
(3)面對(duì)更大的流量時(shí),通過(guò)垂直拆分、服務(wù)化、反向代理、開(kāi)發(fā)框架(站點(diǎn)/服務(wù))等等手段,可以不斷提升高可用(研發(fā)效率);
(4)在面對(duì)上億級(jí)的流量時(shí),通過(guò)配置中心、柔性服務(wù)、消息總線、自動(dòng)化(回歸,測(cè)試,運(yùn)維,監(jiān)控)來(lái)迎接新的挑戰(zhàn);
更多建議: