一個軟件系統隨著功能越來越多,調用量急劇增長,整個系統逐漸碎片化,越來越無序,最
終無法維護和擴展,所以系統在一段時間的野蠻生長后,也需要及時干預,避免越來越無序。架構的本質就是對系統進行有序化重構,使系統不斷進化
那架構是如何實現無序到有序的呢? 基本的手段就是分和合,先把系統打散,然后重新組合。分的過程是把系統拆分為各個子系統 / 模塊 / 組件,拆的時候,首先要解決每個組件的定位問題,然后才能劃分彼此的邊界,實現合理的拆分。合就是根據最終要求,把各個分離的組件有機整合在一起,相對來說,第一步的拆分更難。
拆分的結果使開發人員能夠做到業務聚焦、技能聚焦,實現開發敏捷,合的結果是系統變得柔性,可以因需而變,實現業務敏捷
架構的分類
架構一般可分業務架構、應用架構、技術架構
1. 業務架構從概念層面幫助開發人員更好的理解系統,比如業務流程、業務模塊、輸入輸出、業務域
2. 應用架構從邏輯層面幫助開發落地系統,如數據交互關系、應用形式、交互方式,是的整
個系統邏輯上更容易理解,步入大家熟知的 SOA 就屬于應用架構的范疇
3. 技術架構主要解決技術平臺選型、如操作系統、中間件、設備、多機房、水平擴展、高可用等問題
需要注意的是,系統或者架構首先都是為人服務的,系統的有序度高,用用邏輯合理,業務概念清晰是第一位。現在大家討論更多的是技術架構,如高并發設計,分布式事務處理等,只是因為這個不需要業務上下文背景,比較好相互溝通。具體架構設計時,首先要關注業務架構和應用架構,這個架構新手要特別注意。也是面試時候的痛點!
大型網站的架構演進
ps : 以下內容部分圖片參考自《大型網站系統與 JAVA 中間件實戰.pdf》
從一個電商網站開始
為了更好的理解,我們用電商網站來舉例,作為一個交易類型的網站,一定會具備
用戶(用戶注冊、用戶管理)、商品(商品展示、商品管理)、交易(下單、支付)這些功能假如我們只需要支持這幾個基本功能,那么我們最開始的架構應該可能是這樣的
這個地方要注意的是,各個功能模塊之間是通過 JVM 內部的方法調用來進行交互的,而應用和數據庫之間是通過 JDBC 進行訪問。
單機負載告警,數據庫與應用分離
隨著網站的開放,訪問量不斷增大,那么這個時候服務器的負載勢必會持續升高,必須要才需一些辦法來應付。這里先不考慮更換機器和各種軟件層面的優化,先從架構的結構上來做一些調整。我們可以把數據庫與應用從一臺機器分到兩臺機器
變化:
網站從一臺變成了 2 臺,這個變化對我們來說影響非常小。單機的情況下,我們應用采用 JDBC 的方式來和數據庫進行連接,現在數據庫與應用分開了,我們只需要在配置文件中把數據庫的地址從本機改成數據庫服務器的 ip 地址就行。
對于開發、測試、部署都沒有影響調整以后我們能夠緩解當前的系統壓力,不過隨著時間的退役,訪問量繼續增大的話,我們的系統還是需要做改造
為什么這么分呢?從計算機本身的角度來考慮的話,一個請求的訪問到處理最終到返回,性 能瓶頸只會是:CPU、文件 IO、網絡 IO、內存、等因素。而一臺計算機中這些緯度是有性 能瓶頸的,如果某個資源消耗過多,通常會造成系統的響應速度較慢,所以增加一臺機器, 使得數據庫的 IO 和 CPU 資源獨占一臺機器從而增加性能。 這個地方插入一點題外話,就是簡單說一下各個資源的消耗原因。 CPU/IO/內存: 1. 主要是上下文的切換,因為每個 CPU 核心在同一時刻只能執行一個線程,而 CPU 的調 度有幾種方式,比如搶占式和輪詢等,以搶占式為例,每個線程會分配一定的執行時間, 當達到執行時間、線程中有 IO 阻塞或者有高優先級的線程要執行時。CPU 會切換執行其 他線程。而在切換的過程中,需要存儲當前線程的執行狀態并恢復要執行的線程狀態,這 個過程就是上下文切換。比如 IO、鎖等待等場景下也會觸發上下文切換,當上下文切換 過多時會造成內核占用比較多的 CPU。 2. 文件 IO,比如頻繁的日志寫入,磁盤本身的處理速度較慢、都會造成 IO 性能問題 3. 網絡 IO,帶寬不夠 4. 內存,包括內存溢出、內存泄漏、內存不足 實際上不管是應用層的調優也好,還是硬件的升級也好。其實無非就是這幾個因素的調整。 |
應用服務器復雜告警,如何讓應用服務器走向集群
假如說這個時候應用服務器的壓力變大了,根據對應用的檢測結果,可以針對性的對性能壓力大的地方進行優化。我們這里考慮通過水平擴容來進行優化,把單機變為集群
應用服務器從一臺變為兩臺,這兩個應用服務器之間沒有直接的交互,他們都依賴數據
庫對外提供服務,那么這個時候會拋出兩個問題
1. 最終用戶對應兩個應用服務器訪問的選擇對于這個問題,可以采用 DNS 解決,也可以通過負載均衡設備來解決
2. session 的問題?
水平和垂直擴容
對于大型的分布式架構而言,我們一直在追求一種簡單、優雅的方式來應對訪問量和數據量的增長。而這種方式通常指的是不需要改動軟件程序,僅僅通過硬件升級或者增加機器就可以解決。而這種就是分布式架構下的伸縮設計
伸縮分為垂直伸縮和水平伸縮兩種
垂直伸縮:表示通過升級或者增加單臺機器的硬件來支撐訪問量以及數據量增長的方式,垂直伸縮的好處在于技術難度比較低,運營和改動成本也相對較低。但是缺點是機器性能是有瓶頸的,同時升級高性能的小型機或者大型機,成本是非常大的。這也是阿里去 IOE 的一個原因之一
增加 CPU 核心數:增加 CPU 后系統的服務能力能夠得到大的增長,比如響應速度、同時可以處理的線程數。但是引入 CPU 后也會帶來一些顯著的問題
1. 鎖競爭加劇;多個線程同時運行訪問某個共享數據,那么就涉及到鎖競爭,鎖競爭激烈時
會導致很多線程都在等待鎖,所以即時增加 CPU 也無法讓線程得到更快的處理。當然這里是有調優手段的,可以通過調優手段來降低鎖競爭
2. 支撐并發請求的線程數是固定的,那么即時增加 CPU,系統的服務能力也不會得到提升3. 對于單線程任務,多核心 CPU 是沒有太大的作用的
增加內存:增加內存可以直接提成系統的響應速度,當然,也有可能達不到效果,就是如果JVM 堆內存是固定的。
水平伸縮:通過增加機器來支撐訪問量及數據量增長的方式,成為水平伸縮,水平伸縮理論上來說沒有瓶頸,但是缺點是技術要求比較高,同時給運維帶來了更大的挑戰
垂直伸縮和水平伸縮都有各自的有點,我們在實際使用過程中都會對兩者做結合,一方面要考慮硬件升級的成本,一方面要考慮軟件改造的成本。
引入負載均衡設備
服務路由,基于負載均衡設備來實現
引入負載均衡器以后,會帶來 session 相 關的問題
負載均衡算法
輪詢(Round Robin)法
將請求按順序輪流分配到后臺服務器上,均衡的對待每一臺服務器,而不關心服務器實際的連接數和當前的系統負載
缺點:當集群中服務器硬件配置不同、性能差別大時,無法區別對待
隨機法
通過系統隨機函數,根據后臺服務器列表的大小值來隨機選取其中一臺進行訪問。隨著調用量的增大,其實際效果越來越接近于平均分配流量到后臺的每一臺服務器,也就是輪詢法的效果
優點:簡單使用,不需要額外的配置和算法。
缺點:隨機數的特點是在數據量大到一定量時才能保證均衡,所以如果請求量有限的話,可能會達不到均衡負載的要求。
源地址哈希法
根據服務消費者請求客戶端的 IP 地址,通過哈希函數計算得到一個哈希值,將這個哈希值和服務器列表的大小進行取模運算,得到的結果便是要訪問的服務器地址的序號。采用源地址哈希法進行負載均衡,相同的 IP 客戶端,如果服務器列表不變,將映射到同一個后臺服務器進行訪問。
加權輪詢(Weight Round Robin)法
不同的后臺服務器可能機器的配置和當前系統的負載并不相同,因此它們的抗壓能力也不一樣。跟配置高、負載低的機器分配更高的權重,使其能處理更多的請求,而配置低、負載高的機器,則給其分配較低的權重,降低其系統負載,加權輪詢很好的處理了這一問題,并將請求按照順序且根據權重分配給后端
最小連接數法
前面幾種方式都是通過對請求次數的合理分配最大可能提高服務器的利用率,但是實際上,請求次數的均衡并不能代表負載的均衡。所以,引入了最小連接數法。它正是根據后端服務器當前的連接情況,動態的選取其中當前積壓連接數最少的一臺服務器來處理當前請求,盡可能的提高后臺服務器利用率,將負載合理的分流到每一臺服務器。