前言以淘寶網(wǎng)為例,簡單了解一下大型電商的服務(wù)端架構(gòu)是怎樣的。如圖所示 除圖中所示之外還包含一些我們看不到的,比如高可用的體現(xiàn)。淘寶目前已經(jīng)實(shí)現(xiàn)多機(jī)房容災(zāi)和異地機(jī)房單元化部署,為淘寶的業(yè)務(wù)也提供了穩(wěn)定、高效和易于維護(hù)的基礎(chǔ)架構(gòu)支撐。 這是一個(gè)含金量非常高的架構(gòu),也是一個(gè)非常復(fù)雜而龐大的架構(gòu),當(dāng)然這個(gè)架構(gòu)不是一天兩天演進(jìn)成這樣的,也不是一開始就設(shè)計(jì)并開發(fā)成這樣的,對于初創(chuàng)公司而言,很難在初期就預(yù)估到未來流量千倍、萬倍的網(wǎng)站架構(gòu)會(huì)是怎樣的狀況,同時(shí)如果初期就設(shè)計(jì)成千萬級(jí)并發(fā)的流量架構(gòu),也很難去支撐這個(gè)成本。 因此一個(gè)大型服務(wù)系統(tǒng),都是從小一步一步走過來的,在每個(gè)階段找到對應(yīng)該階段網(wǎng)站架構(gòu)所面臨的問題,然后不斷解決這些問題,在這個(gè)過程中,整個(gè)架構(gòu)會(huì)一直演進(jìn),同時(shí)內(nèi)含的代碼也就會(huì)演進(jìn),大到架構(gòu)、小到代碼都是在不斷演進(jìn)和優(yōu)化的。所以說高大上的項(xiàng)目技術(shù)架構(gòu)和開發(fā)設(shè)計(jì)實(shí)現(xiàn)不是一蹴而就的,這是所謂的萬丈高樓平地起。 單機(jī)架構(gòu)從一個(gè)小網(wǎng)站說起,一般來說初始一臺(tái)服務(wù)器就夠了,文件服務(wù)器、數(shù)據(jù)庫以及應(yīng)用都部署在一臺(tái)機(jī)器上。也就是俗稱的 多機(jī)部署隨著網(wǎng)站用戶逐漸增多,訪問量越來越大,硬盤、cpu、內(nèi)存等開始吃緊,一臺(tái)服務(wù)器難以支撐??匆幌卵葸M(jìn)過程,我們將數(shù)據(jù)服務(wù)和應(yīng)用服務(wù)進(jìn)行分離,給應(yīng)用服務(wù)器配置更好的 cpu、內(nèi)存等等,而給數(shù)據(jù)服務(wù)器配置更好、更快的大的硬盤,如圖所示用了三臺(tái)服務(wù)器進(jìn)行部署,能提高一定的性能和可用性。 分布式緩存隨著訪問的并發(fā)越來越高,為了降低接口的訪問時(shí)間提高服務(wù)性能,繼續(xù)對架構(gòu)進(jìn)行演進(jìn)。 我們發(fā)現(xiàn)有很多業(yè)務(wù)數(shù)據(jù)不需要每次都從數(shù)據(jù)庫中獲取,于是我們使用了緩存,因?yàn)?80% 的業(yè)務(wù)訪問都集中在 20% 的數(shù)據(jù)上 (二八原則),如果能將這部分?jǐn)?shù)據(jù)緩存下來,性能就能提高很多,緩存又分兩種,一種是 Application 中的本地緩存,還有遠(yuǎn)程緩存,遠(yuǎn)程緩存又分為遠(yuǎn)程的單機(jī)式緩存和分布式緩存 (圖所示的是分布式緩存集群)。 我們需要思考幾點(diǎn),具有哪種業(yè)務(wù)特點(diǎn)的數(shù)據(jù)使用緩存,具有哪種業(yè)務(wù)特點(diǎn)的數(shù)據(jù)使用本地緩存,具有哪種業(yè)務(wù)特點(diǎn)的數(shù)據(jù)使用遠(yuǎn)程緩存。分布式緩存在擴(kuò)容時(shí)會(huì)遇上什么問題,如何解決,分布式緩存的算法都有哪幾種,都有什么優(yōu)缺點(diǎn)。這些問題都是我們在使用這個(gè)架構(gòu)時(shí)需要思考并解決的問題。 服務(wù)器集群這個(gè)時(shí)候隨著訪問的 qps 不斷提高,假設(shè)我們使用的 Application Server 是 tomcat,那么 tomcat 服務(wù)器的處理能力就會(huì)成為一個(gè)瓶頸,雖然我們也可以通過購買更強(qiáng)大的硬件但總會(huì)有上限,并且這個(gè)成本到后期是呈指數(shù)級(jí)的增長。 這時(shí)候就可以對服務(wù)器做一個(gè)集群 此時(shí)我們又需要思考幾個(gè)問題, 負(fù)載均衡的調(diào)度策略都有哪些,各有什么優(yōu)缺點(diǎn),各適合什么場景,比如輪詢、權(quán)重、地址散列,地址散列又分為原 IP 地址散列、目標(biāo) IP 地址散列、最小連接、加權(quán)最小連接等等。 服務(wù)器集群后,假設(shè)我們登陸了 A 服務(wù)器,session 信息存放在 A 服務(wù)器上了,如果我們的負(fù)載均衡策略是輪詢或者最小連接等,下次是有可能訪問到 B 服務(wù)器,這時(shí)候存儲(chǔ)在 A 服務(wù)器上的 session 信息我們在 B 服務(wù)器是讀取不到的,所以我們需要解決 session 管理的問題。 Session 共享解決方案session sticky我們使用 如圖所示客戶端 1 通過負(fù)載均衡會(huì)固定轉(zhuǎn)發(fā)到服務(wù)器 1 中。缺點(diǎn)是第一假設(shè)有一臺(tái)服務(wù)器重啟了,那么該服務(wù)器的 session 將全部消失,第二是我們的負(fù)載均衡服務(wù)器成了一種有狀態(tài)的服務(wù)器,要實(shí)現(xiàn)容災(zāi)會(huì)有麻煩。 session 復(fù)制session 復(fù)制,即當(dāng) browser1 經(jīng)過負(fù)載均衡服務(wù)器把 session 存到 application1 中,會(huì)同時(shí)把 session 復(fù)制到 application2 中,所以多臺(tái)服務(wù)器都保存著相同的 session 信息。 缺點(diǎn)是應(yīng)用服務(wù)器的帶寬問題,服務(wù)器之間要不斷同步 session 信息,當(dāng)大量用戶在線時(shí),服務(wù)器占用內(nèi)存會(huì)過多,不適合大規(guī)則集群,適合機(jī)器不多情況。 基于 cookie基于 cookie,也就是說我們每次都用攜帶 session 信息的 cookie 去訪問應(yīng)用服務(wù)器。缺點(diǎn)是 cookie 的長度是有限制的,cookie 保存在瀏覽器上安全性也是一個(gè)問題。 session 服務(wù)器把 session 做成了一個(gè) session 服務(wù)器,比如可以使用 redis 實(shí)現(xiàn)。這樣每個(gè)用戶訪問到應(yīng)用服務(wù)器,其 session 信息最終都存到 session server 中,應(yīng)用服務(wù)器也是從 session server 中去獲取 session。 要考慮以下幾個(gè)問題,在當(dāng)前架構(gòu)中 session server 是一個(gè)單點(diǎn)的,如何解決單點(diǎn),保證它的可用性,當(dāng)然也可以將 session server 做成一個(gè)集群,這種方式適用于 session 數(shù)量及 web 服務(wù)器數(shù)量大的情況,同時(shí)改成這種架構(gòu)后,在寫應(yīng)用時(shí),也要調(diào)整存儲(chǔ) session 的業(yè)務(wù)邏輯。 數(shù)據(jù)庫讀寫分離在解決了服務(wù)器橫向擴(kuò)展之后,繼續(xù)看數(shù)據(jù)庫,數(shù)據(jù)庫的讀與寫操作都需要經(jīng)過數(shù)據(jù)庫,當(dāng)用戶量達(dá)到一定量時(shí),數(shù)據(jù)庫性能又成為了一個(gè)瓶頸,我們繼續(xù)演進(jìn)。 我們可以使用數(shù)據(jù)庫的讀寫分離,同時(shí)應(yīng)用要接入多數(shù)據(jù)源。通過統(tǒng)一的數(shù)據(jù)訪問模型進(jìn)行訪問。數(shù)據(jù)庫的讀寫分離是將所有的寫操作引入到主庫中 如何支持多數(shù)據(jù)源,如何封裝對業(yè)務(wù)沒有侵入,如何使用目前業(yè)務(wù)使用的 ORM 框架完成主從的讀寫分離,是否需要更換 ORM,各有什么優(yōu)缺點(diǎn),如何取舍都是當(dāng)前這個(gè)架構(gòu)需要考慮的問題。 例如主庫和從庫復(fù)制有沒有延遲,如果我們將主庫和從庫分機(jī)房部署的話,跨機(jī)房傳輸同步數(shù)據(jù)更是一個(gè)問題。另外應(yīng)用對數(shù)據(jù)源的路由問題,這些也是需要思考和解決的點(diǎn)。 CDN 加速與反向代理我們繼續(xù)增加了 分布式文件服務(wù)器這個(gè)時(shí)候我們的文件服務(wù)器又出現(xiàn)了瓶頸,我們將文件服務(wù)器改成了分布式文件服務(wù)器集群,在使用分布式文件系統(tǒng)時(shí),需要考慮幾個(gè)問題,如何不影響部署在線上的應(yīng)用訪問,是否需要業(yè)務(wù)部門幫忙清洗數(shù)據(jù),是否需要備份服務(wù)器,是否需要重新做域名解析等等。 數(shù)據(jù)庫分庫分表這個(gè)時(shí)候我們的數(shù)據(jù)庫又出現(xiàn)了瓶頸,我們選擇專庫專用的形式,進(jìn)行數(shù)據(jù)的垂直拆分,相關(guān)的業(yè)務(wù)獨(dú)用自己的一個(gè)庫,我們解決了寫數(shù)據(jù)并發(fā)量大的問題。 當(dāng)我們把這些表分成不同的庫,又會(huì)帶來一些新的問題。例如跨業(yè)務(wù)和跨庫的事務(wù),可以使用分布式事務(wù),或者去掉事務(wù),或者不追求強(qiáng)事務(wù)。 隨著訪問量過大,數(shù)據(jù)量過大,某個(gè)業(yè)務(wù)的數(shù)據(jù)庫數(shù)據(jù)量和更新量已經(jīng)達(dá)到了單個(gè)數(shù)據(jù)庫的瓶頸了,這個(gè)時(shí)候就需要進(jìn)行數(shù)據(jù)庫的水平拆分,例如把 user 拆分成了 user1 和 user2,就是將同一個(gè)表的數(shù)據(jù)拆分到兩個(gè)數(shù)據(jù)庫當(dāng)中,這個(gè)時(shí)候我們解決了單數(shù)據(jù)庫的瓶頸。 水平拆分時(shí)候又要注意哪些點(diǎn),都有哪幾種水平拆分的方式。進(jìn)行了水平拆分后,又會(huì)遇到幾個(gè)問題,第一 sql 路由的問題,假設(shè)有一個(gè)用戶,我們?nèi)绾沃肋@個(gè)用戶信息是存在了 user1 還是 user2 數(shù)據(jù)庫中,由于分庫了,我們的主鍵策略也會(huì)有所不同,同時(shí)會(huì)面臨分頁的問題,假設(shè)我們要查詢某月份已經(jīng)下單的用戶明細(xì),而這些用戶又分布在 user1 和 user2 庫中,我們后臺(tái)運(yùn)營管理系統(tǒng)對它進(jìn)行展示的時(shí)候還要進(jìn)行分頁。這些都是我們在使用這個(gè)架構(gòu)時(shí)需要解決的問題。 搜索引擎與 NoSQL在網(wǎng)站發(fā)布并進(jìn)行了大規(guī)模的推廣后,導(dǎo)致我們應(yīng)用服務(wù)器的搜索量又飆升,我們把應(yīng)用服務(wù)器的搜索功能單獨(dú)抽取出來做了一個(gè)搜索引擎,同時(shí)部分場景可以使用 后序這里只是簡單舉例,并沒有依據(jù)什么實(shí)際的業(yè)務(wù)場景。事實(shí)上各個(gè)服務(wù)的架構(gòu)是要根據(jù)實(shí)際的業(yè)務(wù)特點(diǎn)進(jìn)行優(yōu)化和演進(jìn)的,所以這個(gè)過程也不是完全相同的。當(dāng)然這個(gè)架構(gòu)也不是最終形態(tài),還存在很多要提升的地方。 例如負(fù)載均衡服務(wù)器目前是一個(gè)單點(diǎn)的,如果負(fù)載均衡服務(wù)器訪問不了,那么后續(xù)的包括服務(wù)器集群等也就無法訪問了。所以可以將負(fù)載均衡服務(wù)器做成集群,然后做一些主從的雙機(jī)熱備,同時(shí)做一個(gè)自動(dòng)切換的解決方案。 在整個(gè)架構(gòu)的演進(jìn)過程中,其實(shí)還包含更多需要關(guān)注的內(nèi)容,比如安全性、數(shù)據(jù)分析、監(jiān)控、反作弊...... 最后,我想說高大上的項(xiàng)目技術(shù)架構(gòu)和開發(fā)設(shè)計(jì)實(shí)現(xiàn)絕不是一僦而就的。 (完) |
|