淺談網路架構及其演變

米奇羅發表於2019-03-23

前言

現在大型網站的架構變得越來越複雜,不過架構的演變過程並不是沒有規律的,它們是在遇到相應問題之後為了解決問題次啊演變出來的。此文首先從軟體的三大型別說起,再簡單介紹各種架構的演變過程。

1. 軟體的三大型別

在零幾年那個時代,軟體主要以單機軟體為主,如畫圖板、五筆打字等,當時學習電腦和打字時一個概念,那些不需要聯網的單機軟體就是最開始的軟體。

後來有的程式需要將資料統一管理軟體中使用的資料,所以就將儲存資料的資料庫統一存放在一臺主機中,所有的使用者在需要資料的時都要從主機獲取,這時就分出來了客戶端和服務端。

使用者安裝的軟體叫客戶端(Client),統一管理資料的主機中的軟體就叫服務端(Server),這種結構就叫CS結構。

再後來,這種結構的服務端就不只是管理資料了,另外還可以處理一些業務邏輯。業務放到服務端統一處理可以提供更好的安全性和穩定性,不過服務端的負擔也就加重了。CS結構如下圖。

CS結構

CS結構的程式已經可以完成網路通訊了,不過使用起來有點麻煩,首先時開發者要提供客戶端和服務端兩套軟體;其次每個使用者再使用時都需要單獨安裝客戶端軟體,而且升級的時候也需要每個使用者都就行升級。 為了解決這個問而設計了統一的客戶端,而且預設安裝在使用者電腦裡面,這就是電腦中的瀏覽器(Browser),而且一個瀏覽器可以訪問所有同種型別的網站,當然能它主要用於展示資料,具體業務處理是在不同的服務端進行的,這種結構就叫BS結構。 BS結構如下圖。

BS結構

這就是軟體的三大型別:單機型別、CS型別和BS型別。但BS結構的靈活性和處理效率不如CS結構,所以像QQ、微信、大型遊戲等軟體使用的還是CS結構。

2. 基礎結構並不見簡單

BS結構是最基礎的結構,不過即使這種最基礎的結構的底層是實現並不簡單,網際網路是個錯綜複雜的結構,其中包含的節點不計其數,兩個節點之間的距和連線路線都是不確定的,資料在傳輸過程中還可能會丟失, 所以非常複雜。

不過對於複雜的問題,我們可以將其分為若干簡單問題來解決。BS結構網路傳輸的分解方式有兩種:一種是標準的OSI參考模型,另一種是TCP/IP參考模型。 分層關係與對應關係如下圖。

BS結構網路傳輸

實際使用中更多的是TCP/IP模型,其中的四層模型可簡單理解為:

  • 網路接入層:將需要相互連線的節點接入網路中,從而為資料傳輸提供條件。
  • 網際互聯層:找到傳輸資料的目標節點。
  • 傳輸層:實際傳輸資料。
  • 應用層:使用接收到的資料。

簡單理解,就好像我們在網上買東西,首先確定自己所在的位置有相應的快遞,這就相當於網路接入層,然後告訴賣家地址,地址就相當於網際互聯層,快遞送貨就相當於傳輸層,最後我們收到貨物之後拆包使用就相當於應用層。

由於網路傳輸應用非常廣泛,所以需要大家都遵守規矩,不過網路傳輸中的這些規矩不是強制性的,所以不叫制度也不叫標準,而叫協議。==BS結構中TCP/IP模型中的網路接入層沒有相應的協議,網際互聯層是IP協議,傳輸層是TCP協議,應用層是HTTP協議。當然這些協議,我們也可以人為更改。==

3. 架構演變的起點

當服務端資料流量變得越來越大時,主機就難以應付,這時候就需要將應用程式和資料庫分別放在不同的主機中。

淺談網路架構及其演變

4. 海量資料的解決方案

無論是企業的業務系統還是網際網路上的網站程式都面臨者資料量大的問題,這個問題解不好將嚴重影響系統 的執行速度,下面針對這個問題的解決方法進行系統的介紹。

4.1 快取和頁面靜態化

==資料量大這個問題最直接的解決方案就是使用快取==,快取就是將從資料庫中獲取的結果暫時儲存起來,在下次使用的時候無需重新到資料庫中獲取,這樣就能大大降低資料庫的壓力。

==跟快取相似的另外一種技術叫頁面靜態化==,它在原理上跟快取非常相似,快取是將從資料庫中獲取到的資料(當然也可以是別的任何可以序列化的東西)儲存起來,而頁面靜態化是將程式最後生成的頁面儲存起來,使用頁面靜態化後就不需要每次呼叫都重新生成頁面了,這樣不但不需要查詢資料庫,而且連應用程式處理都省了,所以頁面靜態化同時對資料量大和併發量高兩大問題都有好處。

頁面靜態化可以在程式中使用模板技術生成,如常用的 Freemarker和 Velocity都可以根據模板生成靜態頁面,另外也可以使用快取伺服器在應用伺服器的上一層快取生成的頁面,如可以使用 Squid,另外 Nginx也提供了相應的功能。

4.2 資料庫優化

要解決資料量大的問題,是避不開資料庫優化的資料庫優化的方法非常多,常用的有==表結構優化、SL語句優化、分割槽和分表、索引優化、使用儲存過程代替直接操作==等,另外有時候合理使用冗餘也能獲得非常好的效果。

表結構優化

表結構優化是資料庫中最基礎也是最重要的,如果表結構優化得不合理,就可能導致嚴重的效能問題,具體怎麼設計更合理也沒有固定不變的準則,需要根據實際情況具體處理。

SQL語句優化

SQL語句優化也是非常重要的,基礎的SQL優化是語法層面的優化,不過更重要的是處理邏輯的優化,這也需要根據實際情況具體處理,而且要和索引快取等配合使用。不過SQL優化有一個通用的做法就是,首先要將涉及大資料的業務的SQL語句執行時間詳細記錄下來,其次通過仔細分析日誌(同一條語句對不同條件的執行時間也可能不同,這點也需要仔細分析)找出需要優化的語句和其中的問題,然後再有的放矢地優化,而不是不分重點對每條語句都花同樣的時間和精力優化。

分割槽

當一張表中的資料量變多的時候操作速度就慢了,所以很容易想到的就是將資料分到多個表中儲存,但是這麼做之後操作起來比較麻煩,想操作(增刪改査)一個資料還需要先找到對應的表,如果涉及多個表還得跨表操作。其實在常用的資料庫中可以不分表而達到跟分表類似的效果,那就是分割槽。分割槽就是將一張表中的資料按照一定的規則分到不同的區來儲存,這樣在查詢資料時如果資料的範圍在同一個區內那麼可以只對一個區的資料進行操作,這樣操作的資料量更少,速度更快,而且這種方法對程式是透明的,程式不需要做任何改動。

分表

如果一張表中的資料可以分為幾種固定不變的型別,而且如果同時對多種型別共同操作的情況不多,那麼都可以通過分表來處理,這也需要具體情況具體對待。例如對一個業務系統進行重構開發時就將其中儲存工人工作卡片的資料表分成了三個表,並且對每個表進行分割槽,在同時使用快取(主要用於在儲存和修改時對其他表的資料獲取中,如根據工人Id獲取工人姓名、工人類別、所在單位、所在工段及班組等資訊)、索引、SOL優化等的情況下操作速度比原來提高了100倍以上。

另外一種分表的方法是將一個表中不同型別的欄位分到不同的表中儲存,這麼做最直接的好處就是增刪改資料的時候鎖定的範圍減小了,沒被鎖定的表中的資料不受影響。如果個表的操作頻率很高,在增刪改其中一部分欄位資料的同時另一部分欄位也可能被操作,而且(主要指查詢)用不到被增刪改的欄位,那麼就可以把不同型別的欄位分別儲存到不同的表中,這樣可以減少操作時鎖定資料的範圍。不過這樣分表之後,如果需要查詢完整的資料就得使用多表操作了。

索引優化

索引的大致原理是在資料發生變化(增刪改)的時候就預先按指定欄位的順序排列後儲存到一個類似表的結構中,這樣在查詢索引欄位為條件的記錄時就可以很快地從索引中找到對應記錄的指標並從表中獲取到記錄,這樣速度就快多了。

不過索引也是一把雙刃劍,它在提高査詢速度的同時也降低了增刪改的速度,因為每次資料的變化都需要更新相應的索引。

使用儲存過程代替直接操作

在操作過程複雜而且呼叫頻率高的業務中,可以通過使用儲存過程代替直接操作來提高效率,因為儲存過程只需要編譯一次,而且可以在一個儲存過程裡面做一些複雜的操作。

4.3 分離活躍資料

雖然有些資料總資料量非常大,但是活躍資料並不多,這種情況就可以==將活躍資料單獨儲存起來從而提高處理效率==。比如,對網站來說。使用者很多時候就是這種資料,註冊使用者問題多,但是活躍使用者卻不多,而不活躍的使用者中有的偶爾也會登入網站,因此還不能刪除。這時就可以通過一個定期處理的任務將不活躍的使用者轉移到別的資料表中,在主要操作的數(增刪改表中只儲存活躍使用者,查詢時先從預設表中查詢,如果找不到再從不活躍使用者表中查詢,這樣就可以提高查詢的效率。判斷活躍使用者可以通過最近登入時間,也可以通過指定時間段內登入次數。除了使用者外還有很多這種型別的資料,如一個網站上的文章(特別是新聞類的)、企業業務系統中按時間記錄的資料等。

4.4 批量讀取和延遲修改

批量讀取和延遲修改的原理是通過減少操作的次數來提高效率,如果使用得恰當,效率將會呈數量級提升。

4.5 讀寫分離

讀寫分離的本質是==對資料庫進行叢集==,這樣就可以在高併發的情況下將資料庫的操作分配到多喝資料庫伺服器去處理從而降低單臺伺服器的壓力不過由於資料庫的特殊性——每臺伺服器所儲存的資料都需要一致,所以資料同步就成了資料庫叢集中最核心的問題。如果多臺伺服器都可以寫資料那麼資料同步將變得非常複雜,所以一般情況下是將寫操作交給專門的一臺伺服器處理,這臺專門負責寫的伺服器叫做主伺服器。當主伺服器寫入(增刪改)資料後從底層同步到別的伺服器(從伺服器),讀資料的時候到從伺服器讀取,從伺服器可以有多臺,這樣就可以實現讀寫分離,並且將讀請求分配到多個伺服器處理。主伺服器向從伺服器同步資料時,如果從伺服器數量多,那麼可以讓主伺服器先向其中一部分從伺服器同步資料,第一部分從伺服器接收到資料後再向另外一部分同步。結構如下圖。

讀寫分離

4.6 分散式資料庫

分散式資料庫是將不同的表存放到不同的資料庫中然後再放到不同的伺服器。這樣在處理請求時,如果需要呼叫多個表,則可以讓多臺伺服器同時處理,從而提高處理速度。

資料庫叢集(讀寫分離)的作用是將多個請求分配到不同的伺服器處理,從而減輕單臺服的儲存思務器的壓力,而分散式資料庫是解決單個請求本身就非常複雜的問題,它可以將單個請求分配到多個伺服器處理,使用分散式後的每個節點還可以同時使用讀寫分離,從而組成多個節點群,結構圖如圖。

資料庫叢集

4.7 NoSQL和Hadoop

NoSQL是近年來發展非常迅速的一項技術,它的核心就是==非結構化==。我們一般使用的資料庫(SOL資料庫)都是需要先將表的結構定義出來,一個表有幾個欄位,每個欄位各是什麼型別,然後才能往裡面按照相應的型別儲存資料,而且按照資料庫正規化的規定,一個欄位只資源能儲存單一的資訊,不可以包括多層內容,這就對使用的靈活性帶來了很大的制約, NOSQL些文就是突破了這些條條框框,可以非常靈活地進行操作,另外因為 NOSQL通過多個塊儲存資料的特點,其操作大資料的速度也非常快,這些特性正是現在的網際網路程式最需要的,所以NoSQL發展得非常快。現在 NOSQL主要使用在網際網路的程式中,在企業業務系統中使用的還不多,而且現在 NoSQL還不是很成熟,但由於靈活和高效的特性,NOSQL發展的前景是非常好的。(關於NoSQL的介紹後續瞭解之後再介紹)

Hadoop是==專門針對大資料處理的一套框架==,隨著近年來大資料的流行Hadoop也水漲船高,出世不久就紅得發紫。Hadoop對資料的儲存和處理都提供了相應的解決方案,底層資料的儲存思路類似於分散式加叢集的方案,不過 Hadoop是將同一個表中的資料分成多塊儲存到多個節點(分散式).而且每一塊資料都有多個節點儲存(叢集).這裡叢集除了可以並行處理相同的資料,還可以保證資料的穩定性,在其中一個節點出現問題後資料不 會丟失。這裡的每個節點都不包含一個完整的表的資料,但是一個節點可以儲存多個表的資料,結構圖如圖。

Hadoop結構

5. 高併發的解決方案

除了資料量大,另一個常見的問題就是併發量高,很多架構就是針對這個問題設計出來的,下面分別介紹。

5.1 應用和靜態資源分離

剛開始的時候應用和靜態資源是儲存在一起的,當併發量達到一定程度時就需要將靜態資源儲存到專門的伺服器中,靜態資源主要包括圖片,視訊、js.css和一些資原始檔等,這些檔案因為沒有狀態,所以分離比較簡單,直接存放到相應的伺服器就可以了,一般會使用專門的域名去訪問。通過不同的域名可以讓瀏覽器直接訪問資源伺服器而不需要再訪問應用伺服器了。架構如圖。

應用和靜態資源分離

5.2 頁面快取

頁面快取是將應用生成的頁面快取起來,這樣就不需要每次都重新生成頁面了,從面可以節省大量CPU資源,如果將快取的頁面放到記憶體中速度就更快了。如果使用了 Nginx伺服器就可以使用它自帶的快取功能,當然也可以使用專門的Squid伺服器。頁面快取的預設失效機制一般是按快取時間處理的,當然也可以在修改資料之後手動讓相應快取失效。

5.3 叢集與分散式

叢集和分散式處理都是使用多臺伺服器進行處理的,叢集是每臺伺服器都具有相同的功能,處理請求時呼叫哪臺伺服器都可以,主要起分流的作用,分散式是將不同的業務放到不同的伺服器中,處理一個請求可能需要用到多臺伺服器,這樣就可以提高一個請求的處理速度,而且叢集和分散式也可以同時使用,結構如圖所示。

叢集和分散式

叢集有兩個方式:一種是靜態資源叢集。另一種是應用程式叢集。靜態資源叢集比較簡單,而應用程式叢集就有點複雜了。因為應用程式在處理過程中可能會使用到一些快取的資料,如果叢集就需要同步這些資料,其中最重要的就是 Session, Session同步也是應用程式集 群中非常核心的一個問題。

5.4 反向代理

反向代理指的是客戶端直接訪問的伺服器並不真正提供服務,它從別的伺服器獲取資源,然後將結果返回給使用者的,如圖所示。

反向代理

代理伺服器可以和實際處理請求的伺服器在同一臺主機上,而且一臺反向代理伺服器也可以訪問多臺實際處理請求的伺服器。==反向代理伺服器主要有三個作用==:①可以作為前端伺服器跟實際處理請求的伺服器(如Tomcat)整合;②可以用做負載均衡;③轉發請求,比如,可以將不同型別的資源請求轉發到不同的伺服器去處理,可以將動態資源轉發到Tomcat、PHP等動態程式而將圖片等靜態資源的請求轉發到靜態資源的伺服器,另外也可以在url地址結構發生變化後將新地址轉發到原來的舊地址上。

5.5 CDN

CDN其實是一種特殊的叢集頁面快取伺服器,它和普通叢集的多臺頁面快取伺服器比主要是它==存放的位置和分配請求的方式==有點特殊。CDN的伺服器是分佈在全國各地的,當接收到使用者的請求後會將請求分配到最合適的CDN伺服器節點獲取資料,比如,聯通的使用者會分配到聯通的節點,電信的使用者會分配到電信的節點;另外還會按照地理位置進行分配,北京的使用者會分配到北京的節點,上海的使用者會分配到上海的節點。CDN的每個節點其實就是個頁面快取伺服器,如果沒有請求資源的快取就會從主伺服器獲取,否則直接返回快取的頁面。CDN分配請求的方式比較特殊,它並不是使用普通的負載均衡伺服器來分配的,而是用專門的CDN域名解析伺服器在解析域名的時候就分配好的,一般的做法是在ISP那裡使用CNAME將域名解析到一個特定的域名,然後再將解析到的那個域名用專門的CDN伺服器解析到相應的CDN節點,結構如圖所示。

CDN結構

第二步訪問CDN的DNS伺服器是因為 CNAME記錄的目標域名使用NS記錄指向了CDN的DNS伺服器。CDN的每個節點可能也是叢集了多臺伺服器。CDN的原理並不複雜,不過如果要自己去架設則需要投入大量的資金,現在有專門的CDN服務商,可以直接購買它們的服務。

6. 底層的優化

我們前面講到的所有架構都是建立在最前面介紹的基礎架構之上的,而且很多地方都需要通過網路傳輸資料,如果可以加快網路傳輸的速度,那將會讓整個系統從根本上得到改善網路傳輸資料都是按照各種協議進行的,不過協議並不是不可以改變, Google就邁出了這 步,它制定了Ouic、Spdy等協議來傳輸資料,Ouic比TCP效率高而且比UDP安全,Spdy協議在現有HTTP協議的基礎上增加了很多新特性,提高了傳輸的效率,不過有些特性已經包含到了HTTP/2協議中,而且Google也已經放棄了Spdy而使用HTTP/2了。

7. 小結&參考資料

網站架構的整個演變過程主要是圍繞大資料和高併發這兩個問題展開的,解決的方案主要分為==使用快取和使用多資源==兩種型別。多資源主要指多儲存(包括多記憶體)、多CPU和多網路,對於多資源來說又可以分為單個資源處理一個完整的請求和多個資源合作處理一個請求兩種型別,如多儲存和多CPU中的叢集和分散式,多網路中的CDN和靜態資源分離。理解了整個思路之後就抓住了架構演變的本質,而且自己可能還可以設計出更好的架構。

一個網站具體使用什麼樣的架構需要根據實際需要做出選擇,網站架構並不是竟技場,更不是使用的技術越複雜越好,只要可以滿足自己的需要、可以解決自己所遇到的問題就可以了。要想設計出合理的架構首先需要理解每種架構所針對的問題和它背後的本質,只有這樣才能真正把架構用做解決問題的工具,而不是為了架構而架構,最後問題不一定能解決還浪費了資源。另外在使用複雜架構之前一定要先將自己的業務優化好,這是基礎中的基礎,非常重要!

==無論架構還是協議都要以正確的態度對待,它們都是為了解決特定的問題而設計出來的,我們要認真並且謙虛地學習,不過也不需要將它們當成神聖不可侵犯的東西,它們的本質還是為我們解決問題的工具==。另外這些架構、協議以及相關的產品都是經過實際的考驗可以解決問題的,不過也並不是說它們就是最優的解決方案,我們只有真正理解了它們所針對的問題才能對它們理解得更透徹、使用得更靈活。

參考書籍:《看透Spring MVC》第一章

相關文章