你一定要知道的分佈式架構演化史|乾貨滿滿

一、前言

截止到今年3月份,咱們國家的網民數量已達9.04億。面對如此龐大的用戶規模,各種微服務技術,分佈式架構成爲了當代程序員的必修課。隨着新基建的提出,大數據中心,數據湖的概念也越來越熱。那麼分佈式架構作爲這一切技術的核心點,它又是如何演化而來的呢?下面就讓我們一起追本溯源,從遙遠的2001年開始說起。
在這裏插入圖片描述

二、混沌初現

在咱們互聯網的最早期,用戶量很少,網絡速度也很慢,用單節點的服務器搭建一個網站系統沒有任何問題。可是隨着互聯網的普及,用戶量越來越多,併發負載也隨着越來越高,這時候單節點的服務器就開始承受不了了。

知識點一:爲什麼單節點服務器承受不了高併發負載呢?

以用戶登陸請求Tomcat爲例,Tomcat默認的HTTP實現是採用阻塞式的Socket通信,也就是每個請求都需要創建一個線程來處理。默認Tomcat的最大請求數是150,也就是說同時支持150個併發訪問。當然這是可以配置的,但是最大併發數與硬件性能和CPU數量都是有很大關係的,所以一般情況下一個進程有500個以上線程在搶奪資源的時候,整體性能就已經非常低了。所以當用戶量增多,併發變大,線程越來越多,服務器就會越來越慢,我們稱之爲負載過大。

那麼,如何解決這個問題呢?

由於單進程有最大線程數限制,同時單個server的併發是有上限的,那我們可以在服務器上多啓動幾個server,也就是多搞幾個進程。一個server上限是1000,那我們搞2個不就是2000了嗎?這就是最早期的解決方案。

以登陸爲例,我們希望擴展登陸服務器的併發能力,然後綜合考慮後我們選擇水平擴展的方式,如下在一臺服務器啓動3個server,作爲一個整體來處理用戶登陸請求。解決後的方案雛形如下圖:
在這裏插入圖片描述
但是這樣又會出現一個問題:如果希望3個server都處理用戶登陸請求,那這些server需要完全一樣,但是我們知道進程的端口是不能重複的!也就是說三個server裏面有兩個要改變端口,可是對於用戶來說,他們依然訪問的是之前固定的端口,那兩外兩個server並不會被訪問到。

這個問題該如何解決呢?我們接着往下看。

三、負載均衡器誕生

爲了解決前面的問題,我們不能讓用戶直接去訪問server,而是需要把用戶的所有請求彙總到一起,然後把這些負載均勻的分配給這些server,負責幹這件事的程序,我們命名爲負載均衡器。

引入負載均衡器後,上面的問題得到了解決,到底用戶的請求交給哪一個server處理,完全由負載均衡器來根據server的狀態控制。改進後的架構圖如下所示:
在這裏插入圖片描述
知識點二:如何做到均衡分配呢?

看到均衡分配我們就想到了散列,也就是Hash。咱們技術的世界裏,很多都是相通的,均衡分配也是。比如HashMap根據key的Hash值選擇對應的數組下標,把數據分配到內置的key數組上,有效的降低了過高的key衝突。再比如redis經典的16384,它把集羣的內存分爲16384個slots,然後在放數據的時候會根據hash計算要放入的數據對應slots的索引位置,範圍是0-16383。說到slots也會想到Flink,其實技術都是相通的。

在實際的使用中,這樣的架構還會遇到問題。熟悉Tomcat的小夥伴應該知道session的概念,假如用戶a第一次訪問咱們的服務器,然後被負載均衡器分配給了server1,用戶a的一些信息存放到了server1裏面。然後用戶a離線,過一會再次訪問咱們的服務器,這次可能被分配給了server2。我們知道server1和server2是兩個不同的進程,那我們如何拿到上一次登陸時存放在server1裏面的數據呢?

這就要涉及到進程間數據交互了,我們接着往下看。

四、分佈式架構走向成熟

我們知道進程間數據交互是非常非常慢的,大概比線程間的交互慢了1000倍!同時互聯網早期的進程間交互是存在很多問題的,在那個滿是C++程序員的年代,Java程序員是非常稀有珍貴的,同時那時候的技術也是非常難的。在一批批程序員的艱難探索下,最終誕生了璀璨的新星–Spring!

知識點三:Spring是怎麼誕生的?

爲了解決進程間的數據通信問題,我們現在所熟知的RPC協議在2001年的時候Java工程師根據這個協議可以使用RMI規則來實現,但是由於RMI非常非常難,所以那時候會RMI的Java工程師可以說是千里挑一,薪資自然也高,可以拿到2萬每月,要知道那時候北京每平米的房價也只有1000多塊錢!也正是因爲這樣,在2001開始Java被越來越多的人重視,開始登上歷史舞臺,持續至今。

但是由於RMI實在太難了,不利於推廣,所以後來sun公司基於RMI封裝了EJB框架。雖然EJB相比RMI簡單了很多,但是還是不方便學習,也不方便使用。當時的技術圈還是很淳樸的,大家在抨擊的同時還能給出自己的建議及解決方案。其中最著名的就是Rod Johnson爲了表達自己對EJB的不滿,專門出了一本書《Expert One-On-One J2EE Development Without EJB》在抨擊的同時還表達了自己的觀點和建議,其中最著名的觀點就是:如何讓應用程序能以超出當時大衆所慣於接受的易用性和穩定性與J2EE平臺上的不同組件合作

然後這本書引起了整個技術圈大佬的共鳴,所以一大批技術大佬自願和Rod Johnson一起拓展他在2000年寫出來的spring雛形代碼,在他們一起奮鬥了一年之後,於2004年發佈了Spring的1.0版本,就此一顆璀璨的新星誕生在了Java的星河之中!

4.1、分佈式用戶誕生

下面回到咱們的分佈式架構的演變上面來,怎麼處理前面介紹的進程間數據傳輸問題呢?一共有三種方案:

  1. 當前server去之前存在用戶數據的server上去獲取。由於進程間通信過慢,捨棄;
  2. 某個server保存用戶數據後,同步保存到別的server裏面。由於這樣會造成數據的冗餘,同時進程間通信不可過於頻繁的特性,難以處理用戶頻繁的數據更改需求,且性能較低,所以也捨棄了。
  3. 用戶的請求被分配給某臺server後,保存數據到外部的cache server裏,同時cache server對所有server共享。 cache server就相當於緩存服務器,數據保存在內存裏,面向所有server共享。當用戶訪問某臺server時,它只需要去內存裏獲取數據就可以了。這時候大家應該會聯想到redis,是的,redis處理訂單之類的信息的時候,就充當了緩存服務器的作用。

這種把用戶分佈式處理的結構,我們稱之爲分佈式用戶,架構圖大致如下:
在這裏插入圖片描述

4.2、分佈式系統誕生

一個完整的系統肯定是有很多功能的,比如訂單模塊,商品模塊,支付模塊,後臺管理模塊。如果我們和前面一樣把所有這些模塊放在一個服務器資源裏,由於不同模塊的線程肯定是不一樣的,比如5萬個用戶在看商品,2個管理員在看後臺管理。

這時候2個人的線程肯定是搶不過5萬個人的線程的,那這個問題該如何解決呢?

爲了解決這個問題,同時考慮到單服務器性能存在瓶頸,擴展成本高昂。所以最好的辦法就是水平擴展,多搞幾臺服務器還是比較容易實現的,且成本比較低。我們只能把服務器給拆開了,採用多臺機器來提供服務,每個模塊分配到不同的機器上,這樣就不會存在線程爭奪資源的情況了!

線程的問題解決了,還會有帶寬的問題,比如我們的多臺機器共享某個貸款,商品模塊這個機器用戶量非常大,帶寬佔用很高,這時候即使別的機器用戶少,也會出現阻塞的現象,所以把服務器拆分後,我們還要把網絡給拆分,採用不同的帶寬來徹底解決用戶分佈不均勻的問題。

這種把一個系統拆分爲多臺服務器上運行的多個子系統的架構就是分佈式系統。大致如下圖所示:
在這裏插入圖片描述

同時我們把不同模塊通用的功能給單獨抽取出來,採用前面介紹的負載均衡器來均衡負載給各個模塊集羣就可以了,這種模式其實就是現在流行的微服務的概念。這時候採用負載均衡器有兩種方案:服務端負載均衡和客戶端負載均衡。

其中服務端負載均衡的架構圖如下:
在這裏插入圖片描述
通過上面這個圖我們可以發現,服務器端負載均衡一般採用負載均衡器集羣,這是因爲服務器端負載均衡的壓力非常大,因此一般不進行服務器端負載均衡,而是採用客戶端負載均衡

爲了實現客戶端負載均衡,引入了註冊中心的概念。以登陸爲例,客戶端向註冊中心發送登陸請求,然後註冊中心告訴客戶端可以訪問哪臺機器上的服務,然後客戶端採用輪詢的方式去對應機器上訪問即可。大致的結構圖如下:
在這裏插入圖片描述
比如eureka,zookeeper等都可以作爲註冊中心如上圖所示那樣來使用。不過咱們聯繫一下redis,它是沒有這類註冊中心或者負載均衡器的,那麼它就相當於採用去中心化的方式來處理的數據。

知識點四:什麼是去中心化?

概括的來說,去中心化就相當於沒有負載均衡器,沒有這個中心,每個服務器節點都保留別的節點的信息,客戶端訪問任意一個節點,都可以知道別的節點的信息。

五、總結

當下正處於信息大爆炸的時代,分佈式架構伴隨着我們使用的每一款應用,每一個網站,逐漸成爲了我們必知必會的架構模式。本文我們回溯了分佈式的成因和發展,可以說分佈式的發展壯大正是一批批程序員前赴後繼,遇到問題並解決問題,不斷迭代得到的技術成果,爲所有程序員點贊!
在這裏插入圖片描述

如果您對我的文章感興趣,歡迎關注點贊收藏,如果您有疑惑或發現文中有不對的地方,還請不吝賜教,非常感謝!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章