Tungsten Fabric SDN — SmartNIC vRouter
627
2022-05-29
這兩天在寫一個 Spring Security With JWT(含權(quán)限驗證)的 demo ,后端代碼基本已經(jīng)完成,教程也會在這兩天同步更新出來。前端代碼采用 React 框架寫的,目前只寫了登錄功能,后面完善之后也會開源出來。后端代碼地址:https://github.com/Snailclimb/spring-security-jwt-guide 。
Spring Security 這部分內(nèi)容搞懂還是挺麻煩的,自己將這些東西開源出來也是為了減少入門者的學(xué)習(xí)成本。
今天分享一篇很不錯的互聯(lián)網(wǎng)架構(gòu)演化的文章。
對于一個大型網(wǎng)站,主要有以下幾個特征:
支撐海量數(shù)據(jù)
非常高的訪問量
我們常見的大型網(wǎng)站,如百度、淘寶、京東等,都是一個分布式系統(tǒng)。這么復(fù)雜的系統(tǒng)也不是一天建成的,每個系統(tǒng)都經(jīng)歷了漫長的演變過程。
架構(gòu)演變
在大型網(wǎng)站中,其最核心的功能就是?計算?和?存儲。因此系統(tǒng)演變過程也主要圍繞這兩點進行。
1 單機系統(tǒng)
在網(wǎng)站剛剛起步時,數(shù)據(jù)量、訪問量都非常小,通常情況下,只需一臺應(yīng)用服務(wù)器就可以了。
起步時,我們把所有資源全部打包到部署文件中(如 XXX.war),其中包括
class 文件、依賴 jar等;
js、css、圖片等靜態(tài)資源;
對于用戶上傳文件的場景,直接在服務(wù)器上新建一個目錄,將上傳的文件放置在目錄即可。
然后,將打好的發(fā)布包放到 Web 容器中,比如 Tomcat,最后啟動容器,讓其直接對外提供服務(wù)。
該部署策略有以下幾個特征:
用戶通過瀏覽器直接與 Java 應(yīng)用程序進行交互(通常是 Tomcat);
Java 應(yīng)用程序通過 JDBC 與本機的數(shù)據(jù)庫進行交互(如 MySQL);
如果存在文件讀寫的需求,Java 應(yīng)用程序通過文件接口直接對文件進行操作。
這時,有人會問,Java 應(yīng)用程序直接對外,會不會存在一些安全或性能方面的問題呢?
是的,Tomcat 這種 Web 容器對鏈接的保持能力比較弱,當(dāng)存在大量鏈接時,性能下降很快。同時,Tomcat 并不擅長靜態(tài)資源的處理,對此,我們可以引入 Nginx,以緩解 Tomcat 的壓力。
我們在單機部署基礎(chǔ)上,添加 Nginx,也就有了進階方案。
該方案存在以下特征:
用戶不在直接與 Java 應(yīng)用程序進行交互,而是與 Nginx 進行交互;
Tomcat 掛在 Nginx 后,對動態(tài)請求進行處理;
對于靜態(tài)資源的訪問,通過 Nginx 直接訪問文件系統(tǒng);
當(dāng)有文件寫需求時,通過 Java 應(yīng)用程序直接寫入磁盤。
此時,架構(gòu)顯得清晰很多,但我們發(fā)現(xiàn)一個問題,就是系統(tǒng)對靜態(tài)資源和動態(tài)資源的處理是完全不同的。
對于靜態(tài)資源的處理,相對簡單,只是簡單的文件讀寫。而,動態(tài)請求(也就是我們的業(yè)務(wù)承載者)會隨著業(yè)務(wù)的發(fā)展越來越復(fù)雜。
2 動靜分離部署方案
由于?靜態(tài)請求?與?動態(tài)請求?采用不同的處理策略,我們可以將其進行分離。
該部署方案存在以下特性:
通過不同的域名對?動態(tài)請求?和?靜態(tài)請求?進行分離;
新增?靜態(tài)資源服務(wù)器,專門處理靜態(tài)請求,并在服務(wù)器上部署?Java 應(yīng)用程序,處理文件寫需求;Nginx?只負責(zé)文件的讀操作;
對?動態(tài)請求?進行獨立部署,應(yīng)用程序?qū)⑽募膶懻埱筠D(zhuǎn)發(fā)到靜態(tài)服務(wù)器進行處理;
靜態(tài)資源服務(wù)器功能單一,部署繁瑣,有沒有一種更好的策略呢?
答案就是云服務(wù),比如阿里云的 OSS 提供靜態(tài)資源存儲服務(wù)。CDN 提供訪問加速服務(wù),兩者結(jié)合使用,就得到了一個海量容量并且性能超強的靜態(tài)資源服務(wù)器(集群)。
結(jié)合 OSS 和 CDN,靜態(tài)請求不會成為系統(tǒng)的瓶頸,因此,接下來只對動態(tài)請求進行討論。
隨著系統(tǒng)訪問量的增加,動態(tài)請求出現(xiàn)了明顯的瓶頸。
3 應(yīng)用集群化部署
由于所有的動態(tài)請求全部由一臺應(yīng)用服務(wù)器進行處理,當(dāng)訪問量上升時,這臺服務(wù)就成了系統(tǒng)的瓶頸。此時,我們需要將系統(tǒng)中的多個組件部署到不同的服務(wù)器上。
新部署有以下特征:
對 Nginx 進行獨立部署,形成?Web 集群;
對 Java 應(yīng)用程序進行獨立部署,形成?應(yīng)用集群;
對?數(shù)據(jù)庫?進行獨立部署;
Web 集群?與?應(yīng)用集群?間通過?HTTP?協(xié)議進行交互;
應(yīng)用集群?與?數(shù)據(jù)庫?間通過?JDBC?協(xié)議進行交互。
應(yīng)用集群化,會面臨很多挑戰(zhàn),主要的焦點是如何有效的分配用戶請求。
首先要解決的問題便是,用戶如何將請求發(fā)送到不同的 Nginx 中,最常見的方式便是 DNS 輪詢。
大多域名注冊商都支持多條 A 記錄的解析,其實這就是 DNS 輪詢,DNS 服務(wù)器將解析請求按照 A 記錄的順序,逐一分配到不同的 IP 上,這樣就完成了簡單的負載均衡。
這里的負載均衡器主要指的是 Nginx 的反向代理功能。當(dāng)用戶請求發(fā)送到 Nginx 后,Nginx 需要決定將請求轉(zhuǎn)發(fā)到哪臺應(yīng)用服務(wù)器上。
反向代理(Reverse Proxy)是指以代理服務(wù)器來接受 internet 上的連接請求,然后將請求轉(zhuǎn)發(fā)給內(nèi)部網(wǎng)絡(luò)上的服務(wù)器,并將從服務(wù)器上得到的結(jié)果返回給 internet 上請求連接的客戶端,此時代理服務(wù)器對外就表現(xiàn)為一個反向代理服務(wù)器。
Nginx 對于后臺服務(wù)器配置比較靈活,可以同時配置多臺服務(wù)器,并根據(jù)負載策略將請求分發(fā)給后臺服務(wù)器。
在單機時代,我們的請求只會發(fā)送到同一臺機器上,不存在會話問題。當(dāng)將應(yīng)用集群部署時,用戶的多次請求會發(fā)送到不同的應(yīng)用服務(wù)器上。此時,如何對會話進行同步便是棘手問題。
這種方案主要由 Nginx 處理,讓同樣 session 請求每次都發(fā)送到同一臺服務(wù)器進行處理。
Nginx 會將相同用戶的請求發(fā)送到同一臺應(yīng)用服務(wù)器中。
這是最簡單的策略,但存在一定的問題:
Web 服務(wù)器重啟 Session 丟失;
負載均衡需要進行應(yīng)用層解析(第7層),性能損耗較大;
負載均衡器變?yōu)橐粋€有狀態(tài)的點,不易容災(zāi);
會話問題的根源在于 Session 由多個應(yīng)用維護,我們可以使用某種機制,在多臺 Web 服務(wù)間進行 Session 的數(shù)據(jù)同步。
由 Session 同步器在各個 Java 應(yīng)用程序間完成 Session 的同步,最終使每個服務(wù)器中都存在所有用戶的 Session 數(shù)據(jù)。
這個方案的問題:
造成網(wǎng)絡(luò)開銷;
每臺 Web 服務(wù)器都保存所有的 Session,內(nèi)存開銷大;
我們可以將 Session 從 Web 服務(wù)中抽取出來,并對其進行集中存儲。
將 Session 信息保存到 Session 存儲集群中,Java 應(yīng)用程序不在負責(zé) Session 的存儲。
這個方案的問題:
讀取Session引入了網(wǎng)絡(luò)開銷;
存儲設(shè)施問題影響應(yīng)用;
還可以將 session 數(shù)據(jù)放在 cookie 中,然后在 Web 服務(wù)器上從 cookie 中生成對應(yīng)的 Session 數(shù)據(jù)。
將 Session 數(shù)據(jù)編碼到 Cookie 中,每次 Java 應(yīng)用程序使用 Session 時,都從 Cookie 中重建 Session。
該方案的問題:
受到 Cookie 大小的限制;
存在安全性問題;
每次都攜帶巨大的 Cookie,帶寬消耗嚴重;
每次都進行 Session 數(shù)據(jù)恢復(fù),加大應(yīng)用服務(wù)器的負擔(dān);
隨著系統(tǒng)訪問量的持續(xù)增加,面對大量的數(shù)據(jù)讀取請求,數(shù)據(jù)庫有些不堪重負。
此時,我們需要對數(shù)據(jù)庫進行優(yōu)化。
4 數(shù)據(jù)庫讀寫分離
通常情況下,數(shù)據(jù)庫的讀會帥選成為系統(tǒng)的瓶頸。對此,我們可以使用數(shù)據(jù)庫主從機制,通過添加多個從庫來減緩讀壓力。
與之前部署相比,該架構(gòu)只是為數(shù)據(jù)庫增加了若干個從庫:
對數(shù)據(jù)庫實施?主從?部署策略;
對于數(shù)據(jù)的寫請求,只能在?主庫?上進行;
對于數(shù)據(jù)的讀請求,可以在任意的?從庫?上進行;
主庫與從庫間,通過?數(shù)據(jù)庫同步策略?進行數(shù)據(jù)同步。
由于主庫與從庫間的數(shù)據(jù)同步需要時間,會出現(xiàn)數(shù)據(jù)不一致的情況,這塊是業(yè)務(wù)上需要慎重考慮的一點。
隨著業(yè)務(wù)越來越復(fù)雜,對功能和性能的要求也越來越高,最常見的便是數(shù)據(jù)庫 like 語句性能已經(jīng)無法滿足需求;對于某些熱點數(shù)據(jù)的訪問,其性能也下降很快。
此時,我們需要引入其他組件來有針對性的解決問題。
5 引入搜索和緩存
針對數(shù)據(jù)庫的 like 語句,通常情況下,是通過引入搜索引擎來解決;而熱點數(shù)據(jù)的訪問加速,是通過引入緩存服務(wù)來解決。
該架構(gòu)的特征如下:
添加?搜索集群,用以提升數(shù)據(jù)檢索性能;
添加?緩存集群,用以提升熱點數(shù)據(jù)訪問性能。
在對數(shù)據(jù)查詢進行優(yōu)化后,慢慢的系統(tǒng)的寫性能成為了瓶頸。
此時,需要對數(shù)據(jù)的寫性能進行擴展。
6 數(shù)據(jù)庫分庫分表
隨著數(shù)據(jù)量的增長,寫請求量的增加,數(shù)據(jù)庫的寫入逐漸成為了瓶頸。常規(guī)的寫性能優(yōu)化便是對數(shù)據(jù)庫進行分庫分表。
將不同的業(yè)務(wù)數(shù)據(jù)放到不同的數(shù)據(jù)庫實例中。
把同一個表中的數(shù)據(jù)拆分到多的數(shù)據(jù)庫中。
隨著研發(fā)團隊的規(guī)模越來越多,大家同時在一個項目中進行開發(fā),導(dǎo)致頻繁的沖突和相互影響。
此時,會將整個應(yīng)用程序根據(jù)功能模塊進行拆分,從而形成多個子網(wǎng)站或子頻道。
7 應(yīng)用垂直拆分
面對一個巨無霸式的應(yīng)用,就像面對一團毛線團,總有一種無法下手的感覺。對此,可以將其進行拆分,將其拆分為多個應(yīng)用,每個應(yīng)用獨立開發(fā)、獨立部署、獨立維護。
該部署方案更加靈活,大大降低維護成本。
通過不同的域名或 URL 將整個系統(tǒng)分解為多個子系統(tǒng);
用戶通過瀏覽器將各子系統(tǒng)拼接成一個完整的系統(tǒng);
各系統(tǒng)間存在少量交互,甚至沒有交互;
問題慢慢展現(xiàn)出來,系統(tǒng)間公共部分沒有統(tǒng)一維護點,同樣的功能、同樣的代碼分布在各個系統(tǒng)中。
當(dāng)然,我們可以通過發(fā)布 jar 包的方式,共享功能代碼;但當(dāng) jar 升級時,就需要所有的子系統(tǒng)同步升級,運維開銷巨大。此時,我們需要引入服務(wù)化架構(gòu)。
8 服務(wù)化架構(gòu)
我們可以將通用功能封裝成一個服務(wù),獨立開發(fā)、獨立部署、獨立維護。
在該方案中,我們將業(yè)務(wù)邏輯進行了進一步拆分:
整理各個系統(tǒng)間通用業(yè)務(wù)功能,將其封裝為服務(wù),以承載核心業(yè)務(wù)邏輯,構(gòu)建成?服務(wù)集群;
原來的子系統(tǒng)或子頻道,變成薄薄的一層,不承載核心業(yè)務(wù),只是根據(jù)業(yè)務(wù)流程對業(yè)務(wù)服務(wù)進行編排;
應(yīng)用服務(wù)與業(yè)務(wù)服務(wù)間通過 HTTP 或 其他協(xié)議進行通信,常見的包括 Dubbo、Thrift等。
服務(wù)化解決了系統(tǒng)之間的直接調(diào)用問題,也就是常說的 RPC,整個系統(tǒng)的協(xié)調(diào)點全部由應(yīng)用服務(wù)完成。這種架構(gòu)適用于多種場景,但在一些需要異步處理的極端場景就顯得有心無力了。
此時,我們需要引入消息中間件。
9 引入消息隊列
服務(wù)化解決了直接調(diào)用問題,對于異步調(diào)用,最常見的便是消息中間件。
相比之前的架構(gòu),變化很小,只是在各個業(yè)務(wù)服務(wù)間添加了另外的一種調(diào)用方式。
10 小結(jié)
冰凍三尺非一日之寒,一個大型系統(tǒng)的構(gòu)建也不是一朝一夕的事情。我們需要根據(jù)業(yè)務(wù)情況、數(shù)據(jù)量情況、請求量情況對系統(tǒng)進行合理規(guī)劃。
切記,架構(gòu)不是越復(fù)雜越好,而是“適合自己的便是最好的”。
本文轉(zhuǎn)載自微信公眾號JavaGuide
架構(gòu)設(shè)計 Nginx
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。