轉(zhuǎn)自:https://developer.51cto.com/art/202003/612051.htm
隨著 IT 信息化的普及芜繁,更多的交易放到了網(wǎng)絡上快集,信息量增加和訪問次數(shù)頻繁就是要解決的問題了港令。
因此赴肚,逐漸加入了緩存垂睬、集群等技術(shù)手段。同時對業(yè)務的擴展性和伸縮性的要求也越來越高究履。
高并發(fā)、高可用脸狸、可伸縮最仑、可擴展、夠安全的軟件架構(gòu)一直是架構(gòu)設計追求的目標炊甲。
今天我們來看一下架構(gòu)設計經(jīng)歷了哪些階段泥彤,每個階段都解決了哪些問題,又引出了哪些新問題卿啡。
主要是引起大家的思考吟吝,在不同的業(yè)務發(fā)展階段采取合適技術(shù)手段,用變化擁抱變化是 IT 人追求的目標颈娜。
應用與數(shù)據(jù)一體模式
最早的業(yè)務應用以網(wǎng)站剑逃、OA 等為主,訪問的人數(shù)有限官辽,單臺服務器就能夠應付蛹磺。
通常,將應用程序和數(shù)據(jù)庫部署到一臺服務器上面同仆,如圖 1 所示:
在這一階段萤捆,我們利用 LAMP(Linux Apache MySQL PHP)技術(shù)就可以迅速搞定,并且這些工具都是開源的俗批。
很長一段時間內(nèi)俗或,有各種針對這種應用模式的開源代碼可以使用。這種模式基本上沒有高并發(fā)的要求岁忘,可用性也很差辛慰。
有的服務器采用托管模式,上面就安裝了不同的業(yè)務應用干像,一旦服務器出現(xiàn)問題昆雀,所有的應用就罷工了。
不過其開發(fā)和部署成本相對較低蝠筑,適合剛剛起步的應用服務狞膘。圖 1 就描述了單個應用和數(shù)據(jù)庫運行在單臺服務器的模式,我們稱這種模式為應用與數(shù)據(jù)一體模式什乙。
應用與數(shù)據(jù)分離模式
隨著業(yè)務的發(fā)展挽封,用戶數(shù)和請求數(shù)逐漸上升,服務器的性能出現(xiàn)了問題臣镣。其中比較簡單的解決方案就是增加資源辅愿,將業(yè)務應用和數(shù)據(jù)存儲分開智亮。
其架構(gòu)圖如圖 2 所示:
其中,應用服務器需要處理大量的業(yè)務請求点待,對 CPU 和內(nèi)存有一定要求;而數(shù)據(jù)庫服務器需要對數(shù)據(jù)進行存儲和索引等 IO 操作阔蛉,對磁盤的轉(zhuǎn)速和內(nèi)存會考慮更多。
這樣的分離解決了性能的問題癞埠,我們需要擴展更多的硬件資源讓其各司其職状原,使系統(tǒng)可以處理更多的用戶請求。
雖然業(yè)務上依舊存在耦和苗踪,但硬件層面的分離在可用性上比一體式設計要好很多颠区。
緩存的加入
隨著信息化系統(tǒng)的發(fā)展和使用互聯(lián)網(wǎng)人數(shù)的增多,業(yè)務量通铲、用戶量毕莱、數(shù)據(jù)量都在增長。
我們同時發(fā)現(xiàn)颅夺,用戶會對某些數(shù)據(jù)的請求量特別大朋截,例如新聞、商品信息和熱門消息吧黄。
之前這些信息的獲取方式是依靠數(shù)據(jù)庫质和,因此受到數(shù)據(jù)庫 IO 性能的影響。此時數(shù)據(jù)庫成為了整個系統(tǒng)的瓶頸稚字。
如果再增加服務器的數(shù)量饲宿,恐怕也很難解決,于是緩存技術(shù)就登場了胆描,其架構(gòu)圖如圖 3 所示:
這里提到的緩存技術(shù)分為客戶端瀏覽器緩存瘫想、應用服務器本地緩存和緩存服務器緩存。
①客戶端瀏覽器緩存:當用戶通過瀏覽器請求服務器的時候昌讲,會發(fā)起 HTTP 請求国夜。如果對每次 HTTP 請求進行緩存,那么可以減少應用服務器的壓力短绸。
②應用服務器本地緩存:它使用的是進程內(nèi)緩存车吹,又叫托管堆緩存。以 Java 為例醋闭,這部分緩存放在 JVM 的托管堆上面窄驹,同時會受到托管堆回收算法的影響。
由于它運行在內(nèi)存中证逻,對數(shù)據(jù)的響應速度很快乐埠,通常我們會把熱點數(shù)據(jù)放在這里。
在進程內(nèi)緩存沒有命中的時候,會到緩存服務器中獲取信息丈咐,如果還是沒有命中瑞眼,才會去數(shù)據(jù)庫中獲取。
③緩存服務器緩存:它相對于應用服務器本地緩存來說棵逊,就是進程外緩存伤疙,既可以和應用服務部署在同一服務器,也可以部署到不同的服務器辆影。
一般來說徒像,為了方便管理和合理利用資源,會將其部署到專門的緩存服務器上面秸歧。由于緩存會占用內(nèi)存空間厨姚,因此這類服務器會配置比較大的內(nèi)存衅澈。
圖 3 描述了緩存請求的次序键菱,先訪問客戶端緩存,之后是進程內(nèi)的本地緩存今布,接下來是緩存服務器经备,最后才是數(shù)據(jù)。
如果在任意一層獲取了緩存信息部默,就不再往下訪問了侵蒙,否則會一直按照這個次序獲取緩存信息,直到數(shù)據(jù)庫傅蹂。
用戶請求訪問數(shù)據(jù)的順序為客戶端瀏覽器緩存→應用服務器本地緩存→緩存服務器緩存纷闺。
如果按照以上次序還沒有命中數(shù)據(jù),才會訪問數(shù)據(jù)庫獲取數(shù)據(jù)份蝴。加入緩存的設計犁功,提高了系統(tǒng)的性能。
由于緩存放在內(nèi)存中婚夫,而內(nèi)存的讀取速度比磁盤要快得多浸卦,能夠很快響應用戶請求。
特別針對一些熱點數(shù)據(jù)案糙,優(yōu)勢尤為明顯限嫌。同時,在可用性方面也有明顯的改善时捌。
即使數(shù)據(jù)庫服務器出現(xiàn)短時間的故障怒医,緩存服務器中保存的熱點或者核心數(shù)據(jù)依舊可以滿足用戶暫時的訪問。當然奢讨,后面還會對可用性進行優(yōu)化裆熙。
服務器集群的加入
經(jīng)過前面三個階段的演進,系統(tǒng)對用戶的請求量有了很好的支持。實際上入录,這都是在解決高性能和可用性的問題蛤奥,這一核心問題會一直貫穿整個系統(tǒng)架構(gòu)的演進過程中。
隨著用戶請求量的增加僚稿,另外一個問題又出現(xiàn)了凡桥,那就是并發(fā)。把這兩個字拆開了來看:并蚀同,理解為“一起并行“缅刽,有同時的意思;發(fā),理解為“發(fā)出調(diào)用”蠢络,也就是請求的意思衰猛。
合起來就是多個用戶同時請求應用服務器。如果說原來的系統(tǒng)面對的僅僅只是大數(shù)據(jù)量的話刹孔,那么現(xiàn)在就需要面對多用戶同時請求啡省。
如果還是按照上一個階段的架構(gòu)圖推導,單個應用服務器已經(jīng)無法滿足高并發(fā)的要求了髓霞。
此時卦睹,服務器集群就加入戰(zhàn)場了,其架構(gòu)圖如圖 4 所示:
服務器集群也就是多臺服務器扎堆的意思方库,用更多的服務器來分擔單臺服務器的負載壓力结序,提高性能和可用性。
再說白一點纵潦,就是提高單位時間內(nèi)服務處理請求的數(shù)量徐鹤。原來是一個服務器處理,現(xiàn)在是一堆服務器來處理邀层。就好像銀行柜臺一樣返敬,增加柜員的人數(shù)來服務更多的人。
這次架構(gòu)演進與上次相比被济,增加了應用服務器的個數(shù)救赐,用多臺應用服務器形成集群。
應用服務器中所部署的應用服務沒有改變只磷,在用戶請求與服務器之間加入了負載均衡器经磅,幫助用戶請求路由到對應的服務器中。增加服務器的舉動表明钮追,系統(tǒng)的瓶頸是在處理用戶并發(fā)請求上预厌。
針對數(shù)據(jù)庫和緩存都沒有做更改,這樣僅僅通過增加服務器數(shù)量就能夠緩解請求的壓力元媚。
服務器集群會通過多臺服務器來分擔原來一臺服務器需要處理的請求轧叽,在多臺服務器上同時運行一套系統(tǒng)苗沧,因此可以同時處理大量并發(fā)的用戶請求。
有點三個臭皮匠頂個諸葛亮的意思炭晒,因此對集群中單個服務器的硬件要求也會降低待逞。此時需要注意負載均衡均衡的算法,例如輪詢和加權(quán)輪詢网严。
我們要保證用戶請求能夠均勻分布到服務器上面识樱,同一個會話的請求保證在同一個服務器上面處理,針對不同服務器資源的優(yōu)劣動態(tài)調(diào)整流量震束。
負載均衡器加入之后怜庸,由于其位于互聯(lián)網(wǎng)與應用服務器之間,負責用戶流量的接入垢村,因此可以對用戶流量進行監(jiān)控割疾,同時對訪問用戶的身份和權(quán)限進行驗證。
數(shù)據(jù)庫讀寫分離
加入緩存可以解決部分熱點數(shù)據(jù)的讀取嘉栓,但緩存數(shù)據(jù)的容量有限宏榕,那些非熱點的數(shù)據(jù)依舊會從數(shù)據(jù)庫中讀取。數(shù)據(jù)庫對于寫入和讀取的性能是不一樣的胸懈。
在寫入數(shù)據(jù)的時候担扑,會造成鎖行或者鎖表恰响,此時如果有其他寫入操作并發(fā)執(zhí)行趣钱,會存在排隊現(xiàn)象。
而讀取操作比寫入操作更加快捷胚宦,并且可以通過索引首有、數(shù)據(jù)庫緩存等方式實現(xiàn)。
因此枢劝,推出了數(shù)據(jù)庫讀寫分離的方案井联,其架構(gòu)圖如圖 5 所示:
此時設置了主從數(shù)據(jù)庫,主庫(master)主要用來寫入數(shù)據(jù)您旁,然后通過同步 binlog 的方式烙常,將更新的數(shù)據(jù)同步到從庫(slave)中。
對于應用服務器而言鹤盒,在寫數(shù)據(jù)的時候只需要訪問主庫蚕脏,在讀數(shù)據(jù)的時候只用訪問從庫就好了。
利用數(shù)據(jù)庫讀寫分離的方式侦锯,將數(shù)據(jù)庫的讀/寫職責分離驼鞭。利用讀數(shù)據(jù)效率較高的優(yōu)勢,擴展更多的從庫尺碰,從而服務于讀取操作的用戶請求挣棕。畢竟在現(xiàn)實場景中译隘,大多數(shù)操作都是讀取操作。
此外洛心,從數(shù)據(jù)同步技術(shù)的角度來說固耘,又可以分為同步復制技術(shù)、異步復制技術(shù)和半同步復制技術(shù)词身。在數(shù)據(jù)庫讀寫分離帶來益處的同時玻驻,架構(gòu)也需要考慮可靠性的問題。
例如偿枕,主庫如果掛掉璧瞬,從庫如何接替主庫進行工作。主庫在恢復以后渐夸,是成為從庫還是繼續(xù)擔當主庫嗤锉,以及如何同步數(shù)據(jù)的問題。
反向代理與 CDN
隨著互聯(lián)網(wǎng)的逐漸普及墓塌,人們對網(wǎng)絡安全和用戶體驗的要求也越來越高瘟忱。之前用戶都是通過客戶端直接訪問應用服務器獲取服務,應用服務器會暴露在互聯(lián)網(wǎng)中苫幢,容易遭到攻擊访诱。
如果在應用服務器與互聯(lián)網(wǎng)之間加上一個反向代理服務器,它接收用戶的請求韩肝,然后再轉(zhuǎn)發(fā)到內(nèi)網(wǎng)的應用服務器触菜,充當外網(wǎng)與內(nèi)網(wǎng)之間的緩沖。
反向代理服務器只是做請求的轉(zhuǎn)發(fā)哀峻,在它上面沒有運行任何應用涡相,因此當有人攻擊它的時候,是不會影響到內(nèi)網(wǎng)的應用服務器的剩蟀。
這無形中保護了應用服務器催蝗,提高了安全性。同時育特,它也在互聯(lián)網(wǎng)與內(nèi)網(wǎng)之間起到適配和網(wǎng)速轉(zhuǎn)換的作用丙号。
例如,應用服務器需要服務公網(wǎng)和教育網(wǎng)缰冤,但是兩個網(wǎng)絡的網(wǎng)速不同犬缨,可以在應用服務器與互聯(lián)網(wǎng)之間放上兩臺反向代理服務器,一臺連接公網(wǎng)锋谐,另一臺連接教育網(wǎng)遍尺,屏蔽網(wǎng)絡差異,服務于更多的用戶群體涮拗。
如圖 6乾戏,公網(wǎng)客戶端和校園網(wǎng)客戶端分別來自公網(wǎng)與校園網(wǎng)兩個不同的網(wǎng)絡:
由于兩個網(wǎng)絡訪問速度不同迂苛,因此會針對兩個網(wǎng)絡分別設置共網(wǎng)代理服務器和校園網(wǎng)代理服務器,通過這種方式將位于不通網(wǎng)絡的用戶請求接入到系統(tǒng)中鼓择。
聊完反向代理三幻,再來說說 CDN,它的全稱是 Content Delivery Network呐能,也就是內(nèi)容分發(fā)網(wǎng)絡念搬。
如果把互聯(lián)網(wǎng)想象成一張大網(wǎng)的話,每個服務器或者客戶端就是分布式在網(wǎng)中的一個節(jié)點摆出。
節(jié)點之間的距離有遠有近朗徊,用戶請求會從一個節(jié)點跳轉(zhuǎn)到另外一個節(jié)點,最終跳轉(zhuǎn)到應用服務器獲取信息偎漫。
如果跳轉(zhuǎn)的次數(shù)越少爷恳,就能夠更快地獲取信息,因此可以在離客戶端近的節(jié)點存放信息象踊。
這樣用戶通過客戶端温亲,只需要較少的跳轉(zhuǎn)次數(shù)就能夠觸達信息。由于這部分信息更新頻率不高杯矩,推薦存放一些靜態(tài)數(shù)據(jù)栈虚,例如 JavaScript 文件、靜態(tài)的 HTML史隆、圖片文件等魂务。
這樣客戶端就可以從離自己最近的網(wǎng)絡節(jié)點獲取資源,大大提高了用戶體驗和傳輸效率逆害。
加入 CDN 后的架構(gòu)圖如圖 7 所示:
CDN 的加入明顯加快了用戶訪問應用服務器的速度头镊,同時也減輕了應用服務器的壓力蚣驼,原來必須直接訪問應用服務器的請求魄幕,不用經(jīng)過層層網(wǎng)絡,而只用找到最近的網(wǎng)絡節(jié)點就可以獲取資源颖杏。
但從請求資源的角度上來看纯陨,這種方式也有局限性,它只能對靜態(tài)資源起作用留储,需要定時對 CDN 服務器進行資源更新翼抠。反向代理和 CDN 的加入解決了安全性、可用性和高性能的問題获讳。
分布式數(shù)據(jù)庫與分表分庫
經(jīng)歷前面幾個階段以后阴颖,軟件的系統(tǒng)架構(gòu)相對趨于穩(wěn)定。隨著系統(tǒng)運行時間的增加丐膝,數(shù)據(jù)庫中累積的數(shù)據(jù)越來越多量愧,同時系統(tǒng)還會記錄一些過程數(shù)據(jù)钾菊,例如操作數(shù)據(jù)和日志數(shù)據(jù),這些數(shù)據(jù)也會加重數(shù)據(jù)庫的負擔偎肃。
即便數(shù)據(jù)庫設置了索引和緩存煞烫,但在海量數(shù)據(jù)查詢的時候還會捉襟見肘。如果說讀寫分離累颂,是將數(shù)據(jù)庫從讀寫層面進行資源分配滞详,那么分布式數(shù)據(jù)庫就需要從業(yè)務和數(shù)據(jù)層面對資源進行分配。
①對于數(shù)據(jù)表來說紊馏,當表中包含的記錄過多時料饥,會將其分成多張表來存儲。
例如:有 1000 萬個會員記錄朱监,就可以將其分成兩個 500 萬稀火,分別放到兩個表中存儲。
也可以按照業(yè)務將表中的列進行分割赌朋,把表中的某些列放到其他表中存儲凰狞,然后通過外鍵關(guān)聯(lián)到主表,被分割出去的列通常是不經(jīng)常訪問的數(shù)據(jù)沛慢。
②對于數(shù)據(jù)庫來說赡若,每個數(shù)據(jù)庫能夠承受的最大連接數(shù)和連接池是有上限的。為了提高數(shù)據(jù)訪問效率团甲,會根據(jù)業(yè)務需求對數(shù)據(jù)庫進行分割逾冬,讓不同的業(yè)務訪問不同的數(shù)據(jù)庫。當然躺苦,也可以將相同業(yè)務的不同數(shù)據(jù)放到不同的庫中存儲身腻。
如果將這些數(shù)據(jù)庫資源分別放到不同的數(shù)據(jù)庫服務器中,就是分布式數(shù)據(jù)庫設計了匹厘。
由于數(shù)據(jù)存儲在不同的表/庫中嘀趟,甚至在不同的服務器上面,在進行數(shù)據(jù)庫操作的時候會增加代碼的復雜度愈诚。此時可以加入數(shù)據(jù)庫中間件來消除這些差異她按。
架構(gòu)如圖 8 所示,將數(shù)據(jù)拆分以后分別放在表 1 和表 2 中炕柔,兩張表所在的數(shù)據(jù)庫服務器也各不相同酌泰,庫與庫之間還需要考慮數(shù)據(jù)同步的問題。
由于數(shù)據(jù)的分散部署匕累,要從業(yè)務應用獲取數(shù)據(jù)就需要依靠數(shù)據(jù)庫中間件幫忙陵刹。
數(shù)據(jù)庫的分表分庫以及分布式設計,會帶來性能的提升欢嘿,同時也增大了數(shù)據(jù)庫管理和訪問的難度衰琐。原來只用訪問一張表和一個庫巡验,現(xiàn)在需要跨越多張表和多個庫。
從軟件編程的角度來看碘耳,有一些數(shù)據(jù)庫中間件提供了最佳實踐显设,例如 MyCat 和 Sharding JDBC。
此外辛辨,從數(shù)據(jù)庫服務器管理的角度來看捕捂,需要監(jiān)控服務器的可用性。從數(shù)據(jù)治理的角度來看斗搞,需要考慮數(shù)據(jù)擴容和數(shù)據(jù)治理的問題指攒。
業(yè)務拆分
當解決大數(shù)據(jù)量存儲問題以后,系統(tǒng)就能夠存儲更多的數(shù)據(jù)僻焚,這意味著能夠處理更多的業(yè)務允悦。
業(yè)務量的增加,訪問數(shù)的上升虑啤,是任何一個軟件系統(tǒng)在任何時期都要面臨的嚴峻考驗隙弛。
通過前面幾個階段的學習,我們知道系統(tǒng)提升基本依靠空間換取時間狞山,使用更多的資源和空間處理更多的用戶請求全闷。
隨著業(yè)務的復雜度越來越高,以及高并發(fā)的來臨萍启,一些大廠開始將業(yè)務系統(tǒng)進行切分总珠,分開部署。
此時的架構(gòu)圖如圖 9 所示:
如果說前面的服務器集群模式是將同一個應用復制到不同的服務器上勘纯,那么業(yè)務拆分就是將一個應用拆成多個部署到不同的服務器中局服。
此外,還可以對核心應用進行水平擴展驳遵,將其部署到多臺服務器上淫奔。應用雖然做了拆分,但應用之間仍舊有關(guān)聯(lián)超埋,存在應用之間的調(diào)用搏讶、通信和協(xié)調(diào)問題。
由此也會引入隊列霍殴、服務注冊發(fā)現(xiàn)、消息中心等中間件系吩,它們可以協(xié)助系統(tǒng)管理分布到不同服務器来庭、網(wǎng)絡節(jié)點上的應用。
業(yè)務拆分以后會形成一個個應用服務穿挨,既有基于業(yè)務的服務月弛,例如商品服務肴盏、訂單服務,也有基礎服務帽衙,例如消息推送和權(quán)限驗證菜皂。
這些應用服務連同數(shù)據(jù)庫服務器分布在不同的容器、服務器厉萝、網(wǎng)絡節(jié)點中恍飘,對它們的通信、協(xié)調(diào)谴垫、管理和監(jiān)控都是我們需要解決的問題章母。
分布式與微服務
近幾年,微服務是比較火的架構(gòu)方式翩剪,它將業(yè)務應用進行更加精細化的切割乳怎,使之成為更加小的業(yè)務模塊。
做到模塊的高內(nèi)聚低耦合前弯,每個模塊可以獨立存在蚪缀,由獨立的團隊維護。每個模塊內(nèi)部可以采取特有的技術(shù)恕出,而不用關(guān)心其他模塊的技術(shù)實現(xiàn)椿胯。
模塊通過容器的部署運行,模塊之間通過接口和協(xié)議進行調(diào)用剃根。任何一個模塊都可以將自己公開給其他的模塊調(diào)用哩盲。
同時可以將熱點模塊進行水平擴展,增強系統(tǒng)的性能狈醉。當其中某一個模塊出現(xiàn)問題時廉油,又可以由其他相同的模塊代替其工作,增強了可用性苗傅。
大致總結(jié)下來抒线,微服務擁有以下特點,業(yè)務精細化拆分渣慕、自治性嘶炭、技術(shù)異構(gòu)性、高性能逊桦、高可用眨猎。
它像極了分布式架構(gòu),下面來看看它們的區(qū)別强经,如圖 10 所示:
從概念上理解睡陪,它們都做了“拆”的動作,但在下面這幾個方面存在區(qū)別:
①拆分目的不同:分布式設計是為了解決單體應用資源有限的問題,在一個服務器上無法支撐更高的用戶訪問兰迫,因此將一個應用拆解成不同的部分信殊,然后將其部署到不同服務器上,從而分擔高并發(fā)的壓力汁果。
微服務是對服務組件進行精細化涡拘,目的是更好地解耦,讓服務之間通過組合完成高性能据德、高可用鳄乏、可伸縮、可擴展晋控。
②拆分方式不同:分布式服務架構(gòu)將系統(tǒng)按照業(yè)務和技術(shù)分類進行拆分汞窗,目的是讓拆分的服務負載原來單一服務的業(yè)務。
微服務則是在分布式的基礎上進行更細的拆分赡译,它將服務拆成更小的模塊仲吏,更加專業(yè)化,分工更加精細蝌焚,并且每個小模塊都可以獨立運行裹唆。
③部署方式不同:分布式將服務拆分以后,通常會部署到不同的服務器上只洒。
而微服務也可以將不同的服務模塊放到不同的服務器上许帐,同時它也可以在一個服務器上部署多個微服務,或者同一個微服務的多個備份毕谴,并且多使用容器的方式部署成畦。
雖然分布式與微服務有以上區(qū)別,但從實踐的角度來看涝开,它們都是基于分布式架構(gòu)的思想構(gòu)建的循帐。
微服務是分布式的進化版本,也是分布式的子集舀武。它同樣會遇到服務拆分拄养、服務通信、協(xié)同银舱、管理調(diào)度等問題瘪匿。
總結(jié)
本文按照技術(shù)跟隨業(yè)務變化的思路,描述從單體架構(gòu)到集群寻馏,再到分布式架構(gòu)以及微服務的發(fā)展階段棋弥,講述了每個軟件架構(gòu)階段變化的特點,前后架構(gòu)更替的原因和關(guān)系操软,說明了軟件架構(gòu)發(fā)展會一直隨著業(yè)務發(fā)展的方向變化嘁锯。遵循高性能宪祥,高可用聂薪,伸縮性家乘,擴展性,安全性的架構(gòu)目的藏澳。