作者:Black
出處:https://juejin.im/post/5c7cd6eee51d457c042d4b52
一個網(wǎng)站就是一個應用箫荡,當系統(tǒng)壓力較大時蜻展,只能橫向擴展喉誊,增加多個服務器或者多個容器去做負載均衡,避免單點故障而影響到整個系統(tǒng)纵顾。
集中式最明顯的優(yōu)點就是開發(fā)伍茄,測試,運維會比較方便施逾,不用考慮復雜的分布式環(huán)境敷矫。
弊端也很明顯,系統(tǒng)大而復雜汉额、不易擴展曹仗、難于維護,每次更新都必須更新所有的應用蠕搜。
集中式系統(tǒng)拓撲圖
鑒于集中式系統(tǒng)的種種弊端怎茫,促成了分布式系統(tǒng)的形成,分布式系統(tǒng)背后是由一系列的計算機組成妓灌,但用戶感知不到背后的邏輯轨蛤,就像訪問單個計算機一樣,天然的避免了單機故障的問題虫埂。
應用可以按業(yè)務類型拆分成多個應用或服務祥山,再按結(jié)構分成接口層、服務層掉伏。
我們也可以按訪問入口分缝呕,如移動端澳窑、PC 端等定義不同的接口應用。數(shù)據(jù)庫可以按業(yè)務類型拆分成多個實例岳颇,還可以對單表進行分庫分表照捡。同時增加分布式緩存、消息隊列话侧、非關系型數(shù)據(jù)庫栗精、搜索等中間件。
分布式系統(tǒng)雖好瞻鹏,但是增加了系統(tǒng)的復雜性悲立,如分布式事務、分布式鎖新博、分布式 Session薪夕、數(shù)據(jù)一致性等都是現(xiàn)在分布式系統(tǒng)中需要解決的難題。
分布式系統(tǒng)也增加了開發(fā)測試運維的成本赫悄,工作量增加原献,其管理不好反而會變成一種負擔。
分布式系統(tǒng)最為核心的要屬分布式服務框架埂淮,有了分布式服務框架姑隅,我們只需關注各自的業(yè)務,而無需去關注那些復雜的服務之間調(diào)用的過程倔撞。
分布式服務框架
目前業(yè)界比較流行的分布式服務框架有:阿里的 Dubbo讲仰、Spring Cloud。
這里不對這些分布式服務框架做對比痪蝇,簡單的說說他們都做了些什么鄙陡,能使我們用遠程服務就像調(diào)用本地服務那么簡單高效。
服務
服務是對使用用戶有功能輸出的模塊躏啰,以技術框架作為基礎趁矾,能實現(xiàn)用戶的需求。
比如日志記錄服務给僵、權限管理服務愈魏、后臺服務、配置服務想际、緩存服務、存儲服務溪厘、消息服務等胡本,這些服務可以靈活的組合在一起,也可以獨立運行畸悬。
服務需要有接口侧甫,與系統(tǒng)進行對接。面向服務的開發(fā),應該是把服務拆分開發(fā)披粟,把服務組合運行咒锻。
更加直接的例子如:歷史詳情、留言板守屉、評論惑艇、評級服務等。他們之間能獨立運行拇泛,也要能組合在一起作為一個整體滨巴。
注冊中心
注冊中心對整個分布式系統(tǒng)起著最為核心的整合作用,支持對等集群俺叭,需要提供 CRUD 接口恭取,支持訂閱發(fā)布機制且可靠性要求非常之高,一般拿 Zookeeper 集群來做為注冊中心熄守。
分布式環(huán)境中服務提供方的服務會在多臺服務器上部署蜈垮,每臺服務器會向注冊中心提供服務方標識、服務列表裕照、地址攒发、對應端口、序列化協(xié)議等信息牍氛。
注冊中心記錄下服務和服務地址的映射關系晨继,一般一個服務會對應多個地址,這個過程我們稱之為服務發(fā)布或服務注冊搬俊。
服務調(diào)用方會根據(jù)服務方標識紊扬、服務列表從注冊中心獲取所需服務的信息(地址端口信息、序列化協(xié)議等)唉擂,這些信息會緩存至本地餐屎。
當服務需要調(diào)用其他服務時,直接在這里找到服務的地址玩祟,進行調(diào)用腹缩,這個過程我們稱之為服務發(fā)現(xiàn)。
如下面 Zookeeper 中寫入的臨時順序節(jié)點信息:
com.black.blackrpc.test.HelloWord:發(fā)布服務時對外的名稱空扎。
00000000010藏鹊,00000000011:ZK 順序節(jié)點 ID。
127.0.0.1:8888转锈,127.0.0.1:8889:服務地址端口盘寡。
Protostuff:序列化方式。
1.0:權值撮慨,負載均衡策略使用竿痰。
這里使用的是 Zookeeper 的臨時順序節(jié)點脆粥,為什么使用臨時順序節(jié)點,主要是考慮以下兩點:
當服務提供者異常下線時影涉,與 Zookeeper 的連接會中斷变隔,Zookeeper 服務器會主動刪除臨時節(jié)點,同步給服務消費者蟹倾。
這樣就能避免服務消費者去請求異常的服務器匣缘。校稿注: 一般消費方也會在實際發(fā)起請求前,對當前獲取到的服務提供方節(jié)點進行心跳喊式,避免請求連接有問題的節(jié)點孵户。
Zookeeper?下面是不允許創(chuàng)建 2 個名稱相同的 ZK 子節(jié)點的,通過順序節(jié)點就能避免創(chuàng)建相同的名稱岔留。
當然也可以不用順序節(jié)點的方式夏哭,直接以 com.black.blackrpc.test.HelloWord 創(chuàng)建節(jié)點,在該節(jié)點下創(chuàng)建數(shù)據(jù)節(jié)點献联。
當數(shù)據(jù)同步到本地時竖配,一般會寫入到本地文件中,防止因 Zookeeper 集群異常下線而無法獲取服務提供者信息里逆。
通訊與協(xié)議
服務消費者無論是與注冊中心還是與服務提供者进胯,都需要存在網(wǎng)絡連接傳輸數(shù)據(jù),而這就涉及到通訊原押。
筆者之前也做過這方面的工作胁镐,當時使用的是 java BIO 簡單的寫了一個通訊包,使用場景沒有多大的并發(fā)诸衔,阻塞式的 BIO 也未暴露太多問題盯漂。
java BIO 因其建立連接之后會阻塞線程等待數(shù)據(jù),這種方式必須以一連接一線程的方式笨农,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理就缆。當連接數(shù)過大時,會建立相當多的線程谒亦,性能直線下降竭宰。
Java NIO:同步非阻塞,服務器實現(xiàn)模式為一個請求一個線程份招,即客戶端發(fā)送的連接請求都會注冊到多路復用器上切揭,多路復用器輪詢到連接有 I/O 請求時才啟動一個線程進行處理。
Java AIO:異步非阻塞锁摔,服務器實現(xiàn)模式為一個有效請求一個線程伴箩,客戶端的 I/O 請求都是由 OS 先完成了再通知服務器應用去啟動線程進行處理。
BIO鄙漏、NIO嗤谚、AIO 適用場景分析:
BIO:用于連接數(shù)目比較小且固定的架構,這種方式對服務器資源要求比較高怔蚌,并發(fā)局限于應用中巩步,但程序直觀簡單易理解。
NIO:適用于連接數(shù)目多且連接比較短(輕操作)的架構桦踊,比如聊天服務器椅野,并發(fā)局限于應用中,編程比較復雜籍胯。
目前主流的通訊框架 Netty竟闪、Apache Mina、Grizzl杖狼、NIO Framework 都是基于其實現(xiàn)的炼蛤。
AIO:用于連接數(shù)目多且連接比較長(重操作)的架構,比如圖片服務器蝶涩,文件傳輸?shù)壤砼螅浞终{(diào)用 OS 參與并發(fā)操作,編程比較復雜绿聘。
作為基石的通訊嗽上,其實要考慮很多東西。如:丟包粘包的情況熄攘,心跳機制兽愤,斷連重連,消息緩存重發(fā)挪圾,資源的優(yōu)雅釋放浅萧,長連接還是短連接等。
下面是 Netty 建立服務端洛史,客戶端的簡單實現(xiàn):
說到通訊就不能不說協(xié)議惯殊,通信時所遵守的規(guī)則,訪問什么也殖,傳輸?shù)母袷降榷紝儆趨f(xié)議土思。
作為一個開發(fā)人員,應該都了解 TCP/IP 協(xié)議忆嗜,它是一個網(wǎng)絡通信模型己儒,以及一整套網(wǎng)絡傳輸協(xié)議家族,是互聯(lián)網(wǎng)的基礎通信架構捆毫。
也都應該用過 Http(超文本傳輸協(xié)議)闪湾,Web 服務器傳輸超文本到本地瀏覽器的傳送協(xié)議,該協(xié)議建立在 TCP/IP 協(xié)議之上绩卤。分布式服務框架服務間的調(diào)用也會規(guī)定協(xié)議途样。
為了支持不同場景江醇,分布式服務框架會存在多種協(xié)議,如 Dubbo 就支持 7 種協(xié)議:Dubbo 協(xié)議(默認)何暇,RMI 協(xié)議陶夜,Hessian協(xié)議,HTTP 協(xié)議裆站,WebService 協(xié)議条辟,Thrift 協(xié)議,Memcached 協(xié)議宏胯,Redis 協(xié)議每種協(xié)議應對的場景不盡相同羽嫡,具體場景具體對待。
服務路由
分布式服務上線時都是集群組網(wǎng)部署肩袍,集群中會存在某個服務的多實例杭棵,消費者如何從服務列表中選擇合適的服務提供者進行調(diào)用,這就涉及到服務路由了牛。分布式服務框架需要能夠滿足用戶靈活的路由需求颜屠。
透明化路由
很多開源的 RPC 框架調(diào)用者需要配置服務提供者的地址信息,盡管可以通過讀取數(shù)據(jù)庫的服務地址列表等方式避免硬編碼地址信息鹰祸,但是消費者依然要感知服務提供者的地址信息甫窟,這違反了透明化路由原則。
基于服務注冊中心的服務訂閱發(fā)布蛙婴,消費者通過主動查詢和被動通知的方式獲取服務提供者的地址信息粗井,而不再需要通過硬編碼方式得到提供者的地址信息。
只需要知道當前系統(tǒng)發(fā)布了那些服務街图,而不需要知道服務具體存在于什么位置浇衬,這就是透明化路由。
負載均衡
負載均衡策略是服務的重要屬性餐济,分布式服務框架通常會提供多種負載均衡策略耘擂,同時支持用戶擴展負載均衡策略。
隨機
通常在對等集群組網(wǎng)中絮姆,采用隨機算法進行負載均衡醉冤,隨機路由算法消息分發(fā)還是比較均勻的,采用 JDK 提供的 java.util.Random 或者 java.security.SecureRandom 在指定服務提供者列表中生成隨機地址篙悯。
消費者基于隨機生成的服務提供者地址進行遠程調(diào)用:
隨機還是存在缺點的蚁阳,可能出現(xiàn)部分節(jié)點的碰撞的概率較高,另外硬件配置差異較大時鸽照,會導致各節(jié)點負載不均勻螺捐。
為避免這些問題,需要對服務列表加權,性能好的機器接收的請求的概率應該高于一般機器:
輪詢
逐個請求服務地址定血,到達邊界之后赔癌,繼續(xù)繞接。主要缺點:慢的提供者會累積請求澜沟。
例如第二臺機器很慢届榄,但沒掛。當請求第二臺機器時被卡在那倔喂。久而久之,所有請求都卡在第二臺機器上靖苇。
輪詢策略實現(xiàn)非常簡單席噩,順序循環(huán)遍歷服務提供者列表,達到邊界之后重新歸零開始贤壁,繼續(xù)順序循環(huán)
服務調(diào)用時延
消費者緩存所有服務提供者的調(diào)用時延悼枢,周期性的計算服務調(diào)用平均時延。
然后計算每個服務提供者服務調(diào)用時延與平均時延的差值脾拆,根據(jù)差值大小動態(tài)調(diào)整權重馒索,保證服務時延大的服務提供者接收更少的消息,防止消息堆積名船。?
該策略的特點:保證處理能力強的服務接受更多的消息绰上,通過動態(tài)的權重分配消除服務調(diào)用時延的震蕩范圍,使所有服務的調(diào)用時延接近平均值渠驼,實現(xiàn)負載均衡蜈块。
一致性哈希
相同參數(shù)的請求總是發(fā)送到統(tǒng)一服務提供者,當某一臺服務提供者宕機時迷扇,原本發(fā)往根提供者的請求百揭,基于虛擬節(jié)點,平攤到其他提供者蜓席,不會引起劇烈變動器一,平臺提供默認的虛擬節(jié)點數(shù),可以通過配置文件修改虛擬節(jié)點個數(shù)厨内。
一致性 Hash 環(huán)工作原理如下圖所示:
路由規(guī)則
負載均衡只能保證服務提供者壓力的平衡祈秕,但是在一些業(yè)務場景中需要設置一些過濾規(guī)則,比較常用的是基本表達式的條件路由隘庄。
通過 IP 條件表達式配置黑白名單訪問控制:consumerIP != 192.168.1.1踢步。
只暴露部分服務提供者,防止這個集群服務都被沖垮丑掺,導致其他服務也不可用获印。
序列化與反序列化
把對象轉(zhuǎn)換為字節(jié)序列的過程稱為序列化,把字節(jié)序列恢復為對象的過程稱為反序列化。
運程調(diào)用的時候兼丰,我們需要先將 Java 對象進行序列化玻孟,然后通過網(wǎng)絡,IO 進行傳輸鳍征,當?shù)竭_目的地之后黍翎,再進行反序列化獲取到我們想要的結(jié)果對象。
分布式系統(tǒng)中艳丛,傳輸?shù)膶ο髸芏嘞坏В@就要求序列化速度快,產(chǎn)生字節(jié)序列小的序列化技術氮双。
序列化技術:Serializable碰酝,XML,Jackson戴差,MessagePack送爸,F(xiàn)astJson,Protocol Buffer暖释,Thrift袭厂,Gson,Avro球匕,Hessian 等纹磺。
Serializable 是 Java 自帶的序列化技術,無法跨平臺谐丢,序列化和反序列化的速度相對較慢爽航。
XML 技術多平臺支持好,常用于與銀行交互的報文乾忱,但是其字節(jié)序列產(chǎn)生較大讥珍,不太適合用作分布式通訊框架。
FastJson 是 Java 語言編寫的高性能的 JSON 處理器窄瘟,由阿里巴巴公司開發(fā)衷佃,字節(jié)序列為 json 串,可讀性好蹄葱,序列化也速度非常的快氏义。
Protocol Buffer 序列化速度非常快图云,字節(jié)序列較小惯悠,但是可讀性較差。
一般分布式服務框架會內(nèi)置多種序列化協(xié)議可供選擇竣况,如 Dubbo 支持的 7 種協(xié)議用到的序列化技術就不完全相同克婶。
服務調(diào)用
本地環(huán)境下,使用某個接口很簡單,直接調(diào)用就行情萤。分布式環(huán)境下就不是那么簡單了鸭蛙,消費者方只會存在接口的定義,沒有具體的實現(xiàn)筋岛。
想要像本地環(huán)境下直接調(diào)用遠程接口那就得耗費一些功夫了娶视,需要用到遠程代理。
下面是我盜的圖:
遠程代理
通信時序如下:
通信時序
消費者端沒有具體的實現(xiàn)睁宰,需要調(diào)用接口時動態(tài)的去創(chuàng)建一個代理類肪获。與 Spirng 集成的情況,那直接在 Bean 構建的時候注入代理類柒傻。
下面是構建代理類:
代理會做很多事情贪磺,對請求服務的名稱及參數(shù)信息的序列化、通過路由選擇最為合適服務提供者诅愚、建立通訊連接發(fā)送請求信息(或者直接發(fā)起 Http 請求)、最后返回獲取到的結(jié)果劫映。
當然這里面需要考慮很多問題违孝,如調(diào)用超時,請求異常泳赋,通訊連接的緩存雌桑,同步服務調(diào)用還是異步服務調(diào)用等等。
同步服務調(diào)用:客戶端發(fā)起遠程服務調(diào)用請求祖今,用戶線程完成消息序列化之后校坑,將消息投遞到通信框架,然后同步阻塞千诬,等待通信線程發(fā)送請求并接收到應答之后耍目,喚醒同步等待的用戶線程,用戶線程獲取到應答之后返回徐绑。
異步服務調(diào)用:基于 Java?的 Future 機制邪驮,客戶端發(fā)起遠程服務調(diào)用請求,該請求會被標上 RequestId傲茄,同時建立一個與 RequestId 對應的 Future毅访,客戶端通過 Future 的 Get 方法獲取結(jié)果時會被阻塞。
服務端收到請求應達會回傳 RequestId盘榨,通過 RequestId 去解除對應 Future 的阻塞喻粹,同時 Set 對應結(jié)果,最后客戶端獲取到結(jié)果草巡。
構建 Future守呜,以 RequestId 為 Key,Put 到線程安全的 Map 中。Get 結(jié)果時需要寫入 Time Out 超時時間弛饭,防止由于結(jié)果的未返回而導致的長時間的阻塞柔逼。
結(jié)果返回時通過回傳的?RequestId 獲取對應 Future 寫入 Response蓖谢,F(xiàn)uture 線程解除阻塞:
除了同步服務調(diào)用,異步服務調(diào)用,還有并行服務調(diào)用岸更,泛化調(diào)用等調(diào)用形式。
高可用
簡單的介紹了下分布式服務框架偎箫,下面來說下分布式系統(tǒng)的高可用明吩。一個系統(tǒng)設計開發(fā)出來,三天兩晚就出個大問題拒担,導致無法使用嘹屯,那這個系統(tǒng)也不是什么好系統(tǒng)。
業(yè)界流傳一句話:"我們系統(tǒng)支持 X 個 9 的可靠性"从撼。這個 X 是代表一個數(shù)字州弟,X 個 9 表示在系統(tǒng) 1 年時間的使用過程中,系統(tǒng)可以正常使用時間與總時間(1 年)之比低零。
3 個 9:(1-99.9%)*365*24=8.76 小時婆翔,表示該系統(tǒng)在連續(xù)運行 1 年時間里最多可能的業(yè)務中斷時間是 8.76 小時,4 個 9 即 52.6 分鐘掏婶,5 個 9 即 5.26 分鐘啃奴。要做到如此高的可靠性,是非常大的挑戰(zhàn)雄妥。
一個大型分布式項目可能是由幾十上百個項目構成最蕾,涉及到的服務成千上萬,主鏈上的一個流程就需要流轉(zhuǎn)多個團隊維護的項目老厌。
拿 4 個 9 的可靠性來說瘟则,平攤到每個團隊的時間可能不到 10 分鐘。這 10 分鐘內(nèi)需要頂住壓力枝秤,以最快的時間找到并解決問題壹粟,恢復系統(tǒng)的可用。
下面說說為了提高系統(tǒng)的可靠性都有哪些方案:
服務檢測:某臺服務器與注冊中心的連接中斷宿百,其提供的服務也無響應時趁仙,系統(tǒng)應該能主動去重啟該服務,使其能正常對外提供垦页。
故障隔離:集群環(huán)境下雀费,某臺服務器能對外提供服務,但是因為其他原因痊焊,請求結(jié)果始終異常盏袄。
這時就需要主動將該節(jié)點從集群環(huán)境中剔除忿峻,避免繼續(xù)對后面的請求造成影響,非高峰時期再嘗試修復該問題辕羽。至于機房故障的情況逛尚,只能去屏蔽整個機房了。
目前餓了么做的是異地多活刁愿,即便單邊機房掛了绰寞,流量也可以全量切換至另外一邊機房,保證系統(tǒng)的可用铣口。
監(jiān)控:包含業(yè)務監(jiān)控滤钱、服務異常監(jiān)控、DB 中間件性能的監(jiān)控等脑题,系統(tǒng)出現(xiàn)異常的時候能及時的通知到開發(fā)人員件缸。等到線下報上來的時候,可能影響已經(jīng)很大了叔遂。
壓測:產(chǎn)線主鏈路的壓測是必不可少的他炊,單靠集成測試,有些高并發(fā)的場景是無法覆蓋到的已艰,壓測能暴露平常情況無法出現(xiàn)的問題佑稠,也能直觀的提現(xiàn)系統(tǒng)的吞吐能力。當業(yè)務激增時旗芬,可以考慮直接做系統(tǒng)擴容。
SOP 方案與演練:產(chǎn)線上隨時都可能會發(fā)生問題捆蜀,抱著出現(xiàn)問題時再想辦法解決的態(tài)度是肯定不行的疮丛,時間根本來不及。
提前做好對應問題的 SOP 方案辆它,能節(jié)省大量時間誊薄,盡快的恢復系統(tǒng)的正常。當然平常的演練也是不可少的锰茉,一旦產(chǎn)線故障可以做到從容不迫的去應對和處理呢蔫。
除了上述方案外,還可以考慮服務策略的使用:
降級策略:業(yè)務高峰期飒筑,為了保證核心服務片吊,需要停掉一些不太重要的業(yè)務。
如雙十一期間不允許發(fā)起退款协屡、只允許查看 3 個月之內(nèi)的歷史訂單等業(yè)務的降級俏脊,調(diào)用服務接口時,直接返回的空結(jié)果或異常等服務的降級肤晓,都屬于分布式系統(tǒng)的降級策略爷贫。
服務降級是可逆操作认然,當系統(tǒng)壓力恢復到一定值不需要降級服務時,需要去除降級漫萄,將服務狀態(tài)恢復正常卷员。
服務降級主要包括屏蔽降級和容錯降級:
屏蔽降級:分布式服務框架直接屏蔽對遠程接口的請求,不發(fā)起對遠程服務的調(diào)用腾务,直接返回空結(jié)果毕骡、拋出指定異常、執(zhí)行本地模擬接口實現(xiàn)等方式窑睁。
容錯降級:非核心服務不可調(diào)用時挺峡,可以對故障服務做業(yè)務放通,保證主流程不受影響担钮。如請求超時橱赠、消息解碼異常、系統(tǒng)擁塞保護異常箫津, 服務提供方系統(tǒng)異常等情況狭姨。?
筆者之前就碰到過因雙方?jīng)]有做容錯降級導致的系統(tǒng)故障的情況。午高峰時期苏遥,對方調(diào)用我們的一個非核心查詢接口饼拍,我們系統(tǒng)因為 Bug 問題一直異常,導致對方調(diào)用這個接口的頁面異常而無法跳轉(zhuǎn)到主流程頁面田炭,影響了產(chǎn)線的生產(chǎn)师抄。當時對方緊急發(fā)版才使系統(tǒng)恢復正常。
限流策略:說到限流教硫,最先想到的就是秒殺活動了叨吮,一場秒殺活動的流量可能是正常流量的幾百至幾千倍,如此高的流量系統(tǒng)根本無法處理瞬矩,只能通過限流來避免系統(tǒng)的崩潰茶鉴。
服務的限流本質(zhì)和秒殺活動的限流是一樣的,都是限制請求的流入景用,防止服務提供方因大量的請求而崩潰涵叮。
限流算法:令牌桶、漏桶伞插、計數(shù)器算法割粮。上述算法適合單機的限流,但涉及到整個集群的限流時媚污,得考慮使用緩存中間件了穆刻。
例如:某個服務 1 分鐘內(nèi)只允許請求 2 次,或者一天只允許使用 1000 次杠步。
由于負載均衡存在氢伟,可能集群內(nèi)每臺機器都會收到請求榜轿,這種時候就需要緩存來記錄調(diào)用方某段時間內(nèi)的請求次數(shù),再做限流處理朵锣。Redis 就很適合做此事谬盐。
熔斷策略:熔斷本質(zhì)上是一種過載保護機制,這一概念來源于電子工程中的斷路器诚些,當電流過大時飞傀,保險絲會熔斷,從而保護整個電路诬烹。
同樣在分布式系統(tǒng)中砸烦,當被調(diào)用的遠程服務無法使用時,如果沒有過載保護绞吁,就會導致請求的資源阻塞在遠程服務器上耗盡資源幢痘。
很多時候,剛開始可能只是出現(xiàn)了局部小規(guī)模的故障家破,然而由于種種原因颜说,故障影響范圍越來越大,最終導致全局性的后果汰聋。
當下游服務因訪問壓力過大而響應變慢或失敗门粪,上游服務為了保護自己以及系統(tǒng)整體的可用性,可以暫時切斷對下游服務的調(diào)用烹困。
熔斷器的設計思路:
Closed:初始狀態(tài)玄妈,熔斷器關閉,正常提供服務髓梅。
Open:?失敗次數(shù)拟蜻,失敗百分比達到一定的閾值之后,熔斷器打開女淑,停止訪問服務。
Half-Open:熔斷一定時間之后辜御,小流量嘗試調(diào)用服務鸭你,如果成功則恢復,熔斷器變?yōu)?Closed 狀態(tài)擒权。
數(shù)據(jù)一致性
一個系統(tǒng)設計開發(fā)出來袱巨,必須保證其運行的數(shù)據(jù)準確和一致性。拿支付系統(tǒng)來說:用戶銀行卡已經(jīng)扣款成功碳抄,系統(tǒng)里卻顯示失敗愉老,沒有給用戶的虛擬帳戶充值上,這會引起客訴剖效。
說的再嚴重點嫉入,用戶發(fā)起提現(xiàn)焰盗,資金已經(jīng)轉(zhuǎn)到其銀行賬戶,系統(tǒng)卻沒扣除對應虛擬帳號的余額咒林,直接導致資金損失了熬拒。如果這時候用戶一直發(fā)起提現(xiàn),那就酸爽了垫竞。
CAP 原則
說到數(shù)據(jù)一致性澎粟,就不得不說到 CAP 原則。CAP 原則中指出任何一個分布式系統(tǒng)中欢瞪,Consistency(一致性 C)活烙、 Availability(可用性 A)、Partition tolerance(分區(qū)容錯性 P)遣鼓,三者不可兼得啸盏。
傳統(tǒng)單機數(shù)據(jù)庫基于 ACID 特性(原子性(Atomicity)、一致性(Consistency)譬正、隔離性(Isolation)宫补、持久性(Durability)) ,放棄了分區(qū)容錯性曾我,能做到可用性和一致性粉怕。
對于一個分布式系統(tǒng)而言,分區(qū)容錯性是一個最基本的要求抒巢。既然是一個分布式系統(tǒng)贫贝,那么分布式系統(tǒng)中的組件必然需要被部署到不同的節(jié)點,會出現(xiàn)節(jié)點與節(jié)點之間的網(wǎng)絡通訊蛉谜。
而網(wǎng)絡問題又是一定會出現(xiàn)的異常情況稚晚,分區(qū)容錯性也就成為了一個分布式系統(tǒng)必然需要面對和解決的問題。
系統(tǒng)架構師往往需要把精力花在如何根據(jù)業(yè)務特點在一致性和可用性之間尋求平衡型诚。
集中式系統(tǒng)客燕,通過數(shù)據(jù)庫事務的控制,能做到數(shù)據(jù)的強一致性狰贯。但是分布式系統(tǒng)中也搓,涉及多服務間的調(diào)用,通過分布式事務的方案:
兩階段提交(2PC)
三階段提交(3PC)
補償事務(TCC)
...
雖然能實現(xiàn)數(shù)據(jù)的強一致涵紊,但是都是通過犧牲可用性來實現(xiàn)傍妒。
BASE 理論
BASE 理論是對 CAP 原則中一致性和可用性權衡的結(jié)果:Basically Available(基本可用)、Soft state(軟狀態(tài))和 Eventually consistent(最終一致性)摸柄。
BASE 理論颤练,其來源于對大規(guī)模互聯(lián)網(wǎng)系統(tǒng)分布式實踐的總結(jié)驱负,是基于 CAP 原則逐步演化而來的嗦玖。
其最核心思想是:即使無法做到強一致性患雇,但每個應用都可以根據(jù)自身業(yè)務特點,采用適當?shù)姆绞絹硎瓜到y(tǒng)達到最終一致性踏揣。
基本可用:是指分布式系統(tǒng)在出現(xiàn)不可預知故障的時候庆亡,允許損失部分可用性,這不等價于系統(tǒng)不可用捞稿。
軟狀態(tài):指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài)又谋,并認為該中間狀態(tài)的存在不會影響系統(tǒng)的整體可用性,即允許系統(tǒng)在不同節(jié)點的數(shù)據(jù)副本之間進行數(shù)據(jù)同步的過程存在延時娱局。
最終一致性:強調(diào)的是所有的數(shù)據(jù)副本彰亥,在經(jīng)過一段時間的同步之后,最終都能夠達到一致的狀態(tài)衰齐。
因此任斋,最終一致性的本質(zhì)是需要系統(tǒng)保證最終數(shù)據(jù)能夠達到一致,而不需要實時保證系統(tǒng)數(shù)據(jù)的強一致性耻涛。
總的來說废酷,BASE 理論面向的是大型高可用可擴展的分布式系統(tǒng),和傳統(tǒng)的事物 ACID 特性是相反的抹缕。
它完全不同于 ACID 的強一致性模型澈蟆,而是通過犧牲強一致性來獲得可用性,并允許數(shù)據(jù)在一段時間內(nèi)是不一致的卓研,但最終達到一致狀態(tài)趴俘。
同時,在實際的分布式場景中奏赘,不同業(yè)務單元和組件對數(shù)據(jù)一致性的要求是不同的寥闪,因此在具體的分布式系統(tǒng)架構設計過程中,ACID 特性和 BASE 理論往往又會結(jié)合在一起磨淌。
結(jié)語
分布式系統(tǒng)涉及到的東西還有很多疲憋,如:分布式鎖、定時調(diào)度梁只、數(shù)據(jù)分片缚柳、性能問題、各種中間件的使用等敛纲,筆者分享只是了解到的那一小部分的知識而已喂击。
之前本著學習的目的也寫過一個非常簡單的分布式服務框架 blackRpc剂癌,通過它了解了分布式服務框架內(nèi)部的一些活動淤翔。
本文中所有代碼都能在該項目中找到,有興趣讀者可以看看:
https://github.com/wangshiyu/blackRpc