老板讓我開發(fā)一個(gè)億級(jí)流量的大型網(wǎng)站

對(duì)于一個(gè)大型網(wǎng)站障贸,主要有以下幾個(gè)特征:

  • 支撐海量數(shù)據(jù)

  • 非常高的訪問量

在大型網(wǎng)站中帚戳,其最核心的功能就是計(jì)算和存儲(chǔ)洛巢。因此系統(tǒng)演變過程也主要圍繞這兩點(diǎn)進(jìn)行小染。

單機(jī)系統(tǒng)

在網(wǎng)站剛剛起步時(shí),數(shù)據(jù)量箱舞、訪問量都非常小遍坟,通常情況下,只需一臺(tái)應(yīng)用服務(wù)器就可以了晴股。

單機(jī)部署方案

起步時(shí)愿伴,我們把所有資源全部打包到部署文件中(如 XXX.war),其中包括:

  • class 文件电湘、依賴 jar 等隔节。

  • js、css寂呛、圖片等靜態(tài)資源怎诫。

  • 對(duì)于用戶上傳文件的場(chǎng)景,直接在服務(wù)器上新建一個(gè)目錄贷痪,將上傳的文件放置在目錄即可幻妓。

然后,將打好的發(fā)布包放到 Web 容器中劫拢,比如 Tomcat肉津,最后啟動(dòng)容器,讓其直接對(duì)外提供服務(wù)舱沧。

該部署策略有以下幾個(gè)特征:

  • 用戶通過瀏覽器直接與 Java 應(yīng)用程序進(jìn)行交互(通常是 Tomcat)妹沙。

  • Java 應(yīng)用程序通過 JDBC 與本機(jī)的數(shù)據(jù)庫進(jìn)行交互(如 MySQL)。

  • 如果存在文件讀寫的需求狗唉,Java 應(yīng)用程序通過文件接口直接對(duì)文件進(jìn)行操作初烘。

這時(shí),有人會(huì)問,Java 應(yīng)用程序直接對(duì)外肾筐,會(huì)不會(huì)存在一些安全或性能方面的問題呢哆料?是的,Tomcat 這種 Web 容器對(duì)鏈接的保持能力比較弱吗铐,當(dāng)存在大量鏈接時(shí)东亦,性能下降很快。同時(shí)唬渗,Tomcat 并不擅長靜態(tài)資源的處理典阵,對(duì)此,我們可以引入 Nginx镊逝,以緩解 Tomcat 的壓力壮啊。

單機(jī)部署方案進(jìn)階

我們?cè)趩螜C(jī)部署基礎(chǔ)上,添加 Nginx撑蒜,也就有了進(jìn)階方案:

該方案存在以下特征:

  • 用戶不在直接與 Java 應(yīng)用程序進(jìn)行交互歹啼,而是與 Nginx 進(jìn)行交互。

  • Tomcat 掛在 Nginx 后座菠,對(duì)動(dòng)態(tài)請(qǐng)求進(jìn)行處理狸眼。

  • 對(duì)于靜態(tài)資源的訪問,通過 Nginx 直接訪問文件系統(tǒng)浴滴。

  • 當(dāng)有文件寫需求時(shí)拓萌,通過 Java 應(yīng)用程序直接寫入磁盤。

此時(shí)升略,架構(gòu)顯得清晰很多微王,但我們發(fā)現(xiàn)一個(gè)問題,就是系統(tǒng)對(duì)靜態(tài)資源和動(dòng)態(tài)資源的處理是完全不同的降宅。對(duì)于靜態(tài)資源的處理骂远,相對(duì)簡單囚霸,只是簡單的文件讀寫腰根。而,動(dòng)態(tài)請(qǐng)求(也就是我們的業(yè)務(wù)承載者)會(huì)隨著業(yè)務(wù)的發(fā)展越來越復(fù)雜拓型。

動(dòng)靜分離部署方案

由于靜態(tài)請(qǐng)求與動(dòng)態(tài)請(qǐng)求采用不同的處理策略额嘿,我們可以將其進(jìn)行分離。

該部署方案存在以下特性:

  • 通過不同的域名對(duì)動(dòng)態(tài)請(qǐng)求和靜態(tài)請(qǐng)求進(jìn)行分離劣挫。

  • 新增靜態(tài)資源服務(wù)器册养,專門處理靜態(tài)請(qǐng)求,并在服務(wù)器上部署 Java 應(yīng)用程序压固,處理文件寫需求球拦;Nginx 只負(fù)責(zé)文件的讀操作。

  • 對(duì)動(dòng)態(tài)請(qǐng)求進(jìn)行獨(dú)立部署,應(yīng)用程序?qū)⑽募膶懻?qǐng)求轉(zhuǎn)發(fā)到靜態(tài)服務(wù)器進(jìn)行處理坎炼。

靜態(tài)資源服務(wù)器功能單一愧膀,部署繁瑣,有沒有一種更好的策略呢谣光?

答案就是云服務(wù)檩淋,比如阿里云的 OSS 提供靜態(tài)資源存儲(chǔ)服務(wù)。CDN 提供訪問加速服務(wù)萄金,兩者結(jié)合使用蟀悦,就得到了一個(gè)海量容量并且性能超強(qiáng)的靜態(tài)資源服務(wù)器(集群)。

結(jié)合 OSS 和 CDN氧敢,靜態(tài)請(qǐng)求不會(huì)成為系統(tǒng)的瓶頸日戈,因此,接下來只對(duì)動(dòng)態(tài)請(qǐng)求進(jìn)行討論孙乖。

隨著系統(tǒng)訪問量的增加涎拉,動(dòng)態(tài)請(qǐng)求出現(xiàn)了明顯的瓶頸。

應(yīng)用集群化部署

由于所有的動(dòng)態(tài)請(qǐng)求全部由一臺(tái)應(yīng)用服務(wù)器進(jìn)行處理的圆,當(dāng)訪問量上升時(shí)鼓拧,這臺(tái)服務(wù)就成了系統(tǒng)的瓶頸。

此時(shí)越妈,我們需要將系統(tǒng)中的多個(gè)組件部署到不同的服務(wù)器上季俩。

新部署有以下特征:

  • 對(duì) Nginx 進(jìn)行獨(dú)立部署,形成 Web 集群梅掠。

  • 對(duì) Java 應(yīng)用程序進(jìn)行獨(dú)立部署酌住,形成應(yīng)用集群。

  • 對(duì)數(shù)據(jù)庫進(jìn)行獨(dú)立部署阎抒。

  • Web 集群與應(yīng)用集群間通過 HTTP 協(xié)議進(jìn)行交互酪我。

  • 應(yīng)用集群與數(shù)據(jù)庫間通過 JDBC 協(xié)議進(jìn)行交互。

應(yīng)用集群化且叁,會(huì)面臨很多挑戰(zhàn)都哭,主要的焦點(diǎn)是如何有效的分配用戶請(qǐng)求。

DNS 輪詢

首先要解決的問題便是逞带,用戶如何將請(qǐng)求發(fā)送到不同的 Nginx 中欺矫,最常見的方式便是 DNS 輪詢。

大多域名注冊(cè)商都支持多條 A 記錄的解析展氓,其實(shí)這就是 DNS 輪詢穆趴,DNS 服務(wù)器將解析請(qǐng)求按照 A 記錄的順序,逐一分配到不同的 IP 上遇汞,這樣就完成了簡單的負(fù)載均衡未妹。

負(fù)載均衡器

這里的負(fù)載均衡器主要指的是 Nginx 的反向代理功能簿废。當(dāng)用戶請(qǐng)求發(fā)送到 Nginx 后,Nginx 需要決定將請(qǐng)求轉(zhuǎn)發(fā)到哪臺(tái)應(yīng)用服務(wù)器上络它。反向代理(Reverse Proxy)是指以代理服務(wù)器來接受 Internet 上的連接請(qǐng)求捏鱼,然后將請(qǐng)求轉(zhuǎn)發(fā)給內(nèi)部網(wǎng)絡(luò)上的服務(wù)器,并將從服務(wù)器上得到的結(jié)果返回給 Internet 上請(qǐng)求連接的客戶端酪耕,此時(shí)代理服務(wù)器對(duì)外就表現(xiàn)為一個(gè)反向代理服務(wù)器导梆。Nginx 對(duì)于后臺(tái)服務(wù)器配置比較靈活,可以同時(shí)配置多臺(tái)服務(wù)器迂烁,并根據(jù)負(fù)載策略將請(qǐng)求分發(fā)給后臺(tái)服務(wù)器看尼。

會(huì)話問題

在單機(jī)時(shí)代,我們的請(qǐng)求只會(huì)發(fā)送到同一臺(tái)機(jī)器上盟步,不存在會(huì)話問題藏斩。當(dāng)將應(yīng)用集群部署時(shí),用戶的多次請(qǐng)求會(huì)發(fā)送到不同的應(yīng)用服務(wù)器上却盘。此時(shí)狰域,如何對(duì)會(huì)話進(jìn)行同步便是棘手問題。

①Session Sticky

這種方案主要由 Nginx 處理黄橘,讓同樣 Session 請(qǐng)求每次都發(fā)送到同一臺(tái)服務(wù)器進(jìn)行處理兆览。

Nginx 會(huì)將相同用戶的請(qǐng)求發(fā)送到同一臺(tái)應(yīng)用服務(wù)器中。這是最簡單的策略塞关,但存在一定的問題:

  • Web 服務(wù)器重啟 Session 丟失抬探。

  • 負(fù)載均衡需要進(jìn)行應(yīng)用層解析(第 7 層),性能損耗較大帆赢。

  • 負(fù)載均衡器變?yōu)橐粋€(gè)有狀態(tài)的點(diǎn)小压,不易容災(zāi)。

②Session Replication

會(huì)話問題的根源在于 Session 由多個(gè)應(yīng)用維護(hù)椰于,我們可以使用某種機(jī)制怠益,在多臺(tái) Web 服務(wù)間進(jìn)行 Session 的數(shù)據(jù)同步。

由 Session 同步器在各個(gè) Java 應(yīng)用程序間完成 Session 的同步瘾婿,最終使每個(gè)服務(wù)器中都存在所有用戶的 Session 數(shù)據(jù)蜻牢。這個(gè)方案的問題:

  • 造成網(wǎng)絡(luò)開銷。

  • 每臺(tái) Web 服務(wù)器都保存所有的 Session憋他,內(nèi)存開銷大孩饼。

③集中式 Session

我們可以將 Session 從 Web 服務(wù)中抽取出來,并對(duì)其進(jìn)行集中存儲(chǔ)竹挡。

將 Session 信息保存到 Session 存儲(chǔ)集群中,Java 應(yīng)用程序不在負(fù)責(zé) Session 的存儲(chǔ)立膛。這個(gè)方案的問題:

  • 讀取 Session 引入了網(wǎng)絡(luò)開銷揪罕。

  • 存儲(chǔ)設(shè)施問題影響應(yīng)用梯码。

④Cookie Based Session

還可以將 Session 數(shù)據(jù)放在 Cookie 中,然后在 Web 服務(wù)器上從 Cookie 中生成對(duì)應(yīng)的 Session 數(shù)據(jù)好啰。

將 Session 數(shù)據(jù)編碼到 Cookie 中轩娶,每次 Java 應(yīng)用程序使用 Session 時(shí),都從 Cookie 中重建 Session框往。該方案的問題:

  • 受到 Cookie 大小的限制鳄抒。

  • 存在安全性問題。

  • 每次都攜帶巨大的 Cookie椰弊,帶寬消耗嚴(yán)重许溅。

  • 每次都進(jìn)行 Session 數(shù)據(jù)恢復(fù),加大應(yīng)用服務(wù)器的負(fù)擔(dān)秉版。

隨著系統(tǒng)訪問量的持續(xù)增加贤重,面對(duì)大量的數(shù)據(jù)讀取請(qǐng)求,數(shù)據(jù)庫有些不堪重負(fù)清焕。此時(shí)并蝗,我們需要對(duì)數(shù)據(jù)庫進(jìn)行優(yōu)化。

數(shù)據(jù)庫讀寫分離

通常情況下秸妥,數(shù)據(jù)庫的讀會(huì)成為系統(tǒng)的瓶頸滚停。對(duì)此,我們可以使用數(shù)據(jù)庫主從機(jī)制粥惧,通過添加多個(gè)從庫來減緩讀壓力铐刘。

與之前部署相比,該架構(gòu)只是為數(shù)據(jù)庫增加了若干個(gè)從庫:

  • 對(duì)數(shù)據(jù)庫實(shí)施主從部署策略影晓。

  • 對(duì)于數(shù)據(jù)的寫請(qǐng)求镰吵,只能在主庫上進(jìn)行。

  • 對(duì)于數(shù)據(jù)的讀請(qǐng)求挂签,可以在任意的從庫上進(jìn)行疤祭。

  • 主庫與從庫間,通過數(shù)據(jù)庫同步策略進(jìn)行數(shù)據(jù)同步饵婆。

由于主庫與從庫間的數(shù)據(jù)同步需要時(shí)間勺馆,會(huì)出現(xiàn)數(shù)據(jù)不一致的情況,這塊是業(yè)務(wù)上需要慎重考慮的一點(diǎn)侨核。隨著業(yè)務(wù)越來越復(fù)雜草穆,對(duì)功能和性能的要求也越來越高,最常見的便是數(shù)據(jù)庫 like 語句性能已經(jīng)無法滿足需求搓译;對(duì)于某些熱點(diǎn)數(shù)據(jù)的訪問悲柱,其性能也下降很快。此時(shí)些己,我們需要引入其他組件來有針對(duì)性的解決問題豌鸡。

引入搜索和緩存

針對(duì)數(shù)據(jù)庫的 like 語句嘿般,通常情況下,是通過引入搜索引擎來解決涯冠;而熱點(diǎn)數(shù)據(jù)的訪問加速炉奴,是通過引入緩存服務(wù)來解決。

該架構(gòu)的特征如下:

  • 添加搜索集群蛇更,用以提升數(shù)據(jù)檢索性能瞻赶。

  • 添加緩存集群,用以提升熱點(diǎn)數(shù)據(jù)訪問性能派任。

在對(duì)數(shù)據(jù)查詢進(jìn)行優(yōu)化后砸逊,慢慢的系統(tǒng)的寫性能成為了瓶頸。此時(shí)吨瞎,需要對(duì)數(shù)據(jù)的寫性能進(jìn)行擴(kuò)展痹兜。

數(shù)據(jù)庫分庫分表

隨著數(shù)據(jù)量的增長,寫請(qǐng)求量的增加颤诀,數(shù)據(jù)庫的寫入逐漸成為了瓶頸字旭。常規(guī)的寫性能優(yōu)化便是對(duì)數(shù)據(jù)庫進(jìn)行分庫分表。

垂直拆分

將不同的業(yè)務(wù)數(shù)據(jù)放到不同的數(shù)據(jù)庫實(shí)例中崖叫。

水平切分

把同一個(gè)表中的數(shù)據(jù)拆分到多個(gè)數(shù)據(jù)庫中遗淳。隨著研發(fā)團(tuán)隊(duì)的規(guī)模越來越多,大家同時(shí)在一個(gè)項(xiàng)目中進(jìn)行開發(fā)心傀,導(dǎo)致頻繁的沖突和相互影響屈暗。此時(shí),會(huì)將整個(gè)應(yīng)用程序根據(jù)功能模塊進(jìn)行拆分脂男,從而形成多個(gè)子網(wǎng)站或子頻道养叛。

應(yīng)用垂直拆分

面對(duì)一個(gè)巨無霸式的應(yīng)用,就像面對(duì)一團(tuán)毛線團(tuán)宰翅,總有一種無法下手的感覺弃甥。對(duì)此,可以將其進(jìn)行拆分汁讼,將其拆分為多個(gè)應(yīng)用淆攻,每個(gè)應(yīng)用獨(dú)立開發(fā)、獨(dú)立部署嘿架、獨(dú)立維護(hù)瓶珊。

該部署方案更加靈活,大大降低維護(hù)成本:

  • 通過不同的域名或 URL 將整個(gè)系統(tǒng)分解為多個(gè)子系統(tǒng)耸彪。

  • 用戶通過瀏覽器將各子系統(tǒng)拼接成一個(gè)完整的系統(tǒng)伞芹。

  • 各系統(tǒng)間存在少量交互,甚至沒有交互搜囱。

問題慢慢展現(xiàn)出來丑瞧,系統(tǒng)間公共部分沒有統(tǒng)一維護(hù)點(diǎn)柑土,同樣的功能蜀肘、同樣的代碼分布在各個(gè)系統(tǒng)中绊汹。當(dāng)然,我們可以通過發(fā)布 jar 包的方式扮宠,共享功能代碼西乖;但當(dāng) jar 升級(jí)時(shí),就需要所有的子系統(tǒng)同步升級(jí)坛增,運(yùn)維開銷巨大获雕。此時(shí),我們需要引入服務(wù)化架構(gòu)收捣。

服務(wù)化架構(gòu)

我們可以將通用功能封裝成一個(gè)服務(wù)届案,獨(dú)立開發(fā)、獨(dú)立部署罢艾、獨(dú)立維護(hù)楣颠。

在該方案中,我們將業(yè)務(wù)邏輯進(jìn)行了進(jìn)一步拆分:

  • 整理各個(gè)系統(tǒng)間通用業(yè)務(wù)功能咐蚯,將其封裝為服務(wù)童漩,以承載核心業(yè)務(wù)邏輯,構(gòu)建成服務(wù)集群春锋。

  • 原來的子系統(tǒng)或子頻道矫膨,變成薄薄的一層,不承載核心業(yè)務(wù)期奔,只是根據(jù)業(yè)務(wù)流程對(duì)業(yè)務(wù)服務(wù)進(jìn)行編排侧馅。

  • 應(yīng)用服務(wù)與業(yè)務(wù)服務(wù)間通過 HTTP 或其他協(xié)議進(jìn)行通信,常見的包括 Dubbo呐萌、Thrift 等馁痴。

服務(wù)化解決了系統(tǒng)之間的直接調(diào)用問題,也就是常說的 RPC搁胆,整個(gè)系統(tǒng)的協(xié)調(diào)點(diǎn)全部由應(yīng)用服務(wù)完成弥搞。這種架構(gòu)適用于多種場(chǎng)景,但在一些需要異步處理的極端場(chǎng)景就顯得有心無力了渠旁。此時(shí)攀例,我們需要引入消息中間件。

引入消息隊(duì)列

服務(wù)化解決了直接調(diào)用問題顾腊,對(duì)于異步調(diào)用粤铭,最常見的便是消息中間件。

相比之前的架構(gòu)杂靶,變化很小梆惯,只是在各個(gè)業(yè)務(wù)服務(wù)間添加了另外的一種調(diào)用方式酱鸭。

小結(jié)

冰凍三尺非一日之寒,一個(gè)大型系統(tǒng)的構(gòu)建也不是一朝一夕的事情垛吗。我們需要根據(jù)業(yè)務(wù)情況凹髓、數(shù)據(jù)量情況、請(qǐng)求量情況對(duì)系統(tǒng)進(jìn)行合理規(guī)劃怯屉。切記蔚舀,架構(gòu)不是越復(fù)雜越好,而是“適合自己的便是最好的”锨络。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赌躺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子羡儿,更是在濱河造成了極大的恐慌礼患,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掠归,死亡現(xiàn)場(chǎng)離奇詭異缅叠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拂到,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門痪署,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兄旬,你說我怎么就攤上這事狼犯。” “怎么了领铐?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵悯森,是天一觀的道長。 經(jīng)常有香客問我绪撵,道長瓢姻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任音诈,我火速辦了婚禮幻碱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘细溅。我一直安慰自己褥傍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布喇聊。 她就那樣靜靜地躺著恍风,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朋贬,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天凯楔,我揣著相機(jī)與錄音,去河邊找鬼锦募。 笑死摆屯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的御滩。 我是一名探鬼主播鸥拧,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼党远,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼削解!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沟娱,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤氛驮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后济似,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矫废,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年砰蠢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蓖扑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡台舱,死狀恐怖律杠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竞惋,我是刑警寧澤柜去,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站拆宛,受9級(jí)特大地震影響嗓奢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜浑厚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一股耽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钳幅,春花似錦物蝙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春丽惭,著一層夾襖步出監(jiān)牢的瞬間击奶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工责掏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柜砾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓换衬,卻偏偏與公主長得像痰驱,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瞳浦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容