分散式與微服務

KeBoom發表於2021-06-02

分散式

CAP

C:consistency 一致性 分散式系統能夠同時訪問同一份資料副本

A:availability 可用性 非故障節點能夠在合理時間內獲得合理的結果

P:Partition Tolerance 分割槽容錯性 分散式系統當發生網路分割槽時,服務仍然可用

網路分割槽:分散式網路中,由於某些節點故障,導致系統分成了幾個區域。比如呼叫鏈A->B->C,其中B節點故障,那麼A,B分成了兩個分割槽。

而是在P為條件下,只能實現A或者C。也就是說,在發生網路分割槽後,我們只能保證一致性或者可用性。

當沒有發生網路分割槽時,也就是沒有P為條件,那麼自然可以同時滿足A和C。

zookeeper:保證CP,zookeeper有leader節點,寫操作只能由leader來做,Follower只能處理讀操作,這樣就保證資料的一致性。

Eureka:保證AP,Eureka各個伺服器節點都是平等的,只要有至少一個節點,就能夠保證可用。

Nacos:保證AP和CP,預設支援AP,通過命令進行切換為CP。

BASE理論

  • 基本可用(Basically Available)

    當系統發生不可預知的故障時,允許損失部分可用性。

    • 響應時間的損失:原先0.5s就能返回的結果,允許2s內返回
    • 系統功能的損失:可以損失非核心功能,而保證核心功能的可用
  • 軟狀態(Soft State)

    允許存在中間狀態,允許在多個節點的資料副本存在時延

  • 最終一致性(Eventually Consistent)

    在一個時間期限後,要保證資料的最終一致性

分散式ID

對於單個資料庫我們使用id自增就能保證,id不重複。

對於兩個資料庫,我們分別設定起始值和步長就能避免id重複,但是這時在新增一個資料庫,則要重新修改初始值和步長。

號段模式:我們可以先從資料庫中取出一段資料,比如id為100到200,當需要使用時,在本地直接提取使用,使用完這100個id後,再從資料庫中取200到300的id。

redis:通過redis,在redis中使用incr自增,也能夠保證id的不重複。

雪花演算法:對分散式id的bit進行分配,比如64bit的一個id,從左到右,第一位符號位,接下來41bit為時間戳,接下來10bit為工作機器id,接下來12bit為序列號。這樣就能夠保證id的不重複。

限流演算法

固定視窗計數機演算法

規定一分鐘之內只能有100個請求,如果多了則丟棄該請求。該方法無法防止突增的請求,比如前30秒只有1個請求,後30秒突然進來99個請求,無法保證限流的速率。

滑動視窗計數機演算法

將一分鐘劃分成60個格子,大小為30的視窗不斷移動,每秒的請求放到格子中,如果視窗中的請求超過閾值,就不再處理其他請求。

漏桶演算法

輸出的水流是一定的,這樣就能保證輸出速率穩定。把請求比作水,當請求來時,則將水輸入到水桶上方,即使上方的水是突然激增的,那輸出也仍然是穩定的。如果木桶大小固定,上方水滿了,則水將溢位,也就是說請求丟棄。

缺點是,我們總是以固定的速率流出,我們希望當請求少時,固定速率流出自然沒問題,但是當請求到峰值,我們希望流出速率大一些。

令牌桶演算法

仍然是一個固定大小的桶,我們以固定速率生產令牌,當請求來時,對於大的請求,消耗多點的令牌,小請求就少點令牌。如果桶內令牌沒有了,則丟棄請求。這個演算法就能解決動態去做“流出速率”,峰值時流出速度快,平時呢流出速率平穩。

RPC

做為遠端呼叫框架,目的是希望呼叫遠端方法就像本地方法一樣簡單。

客戶端:通過簡單註解或者使用類呼叫遠端方法

客戶端代理類:通過動態代理,讓客戶端執行的方法最終讓代理類來執行。代理類拿到服務名/方法名,引數等資訊封裝到request中,然後通過網路呼叫傳送給服務端

網路呼叫:最簡單的可以用socket,當然netty更流行,編寫Netty的客戶端與服務端

服務端:Netty中拿到request,知道了方法名,可以通過反射建立實現了該方法的類,然後呼叫之,得到結果後,再通過Netty服務端傳送給客戶端

註冊中心:我們可以用zk,建立節點,節點為服務名,節點下資料為機器ip地址。服務端啟動時,首先將自己能夠提供的服務註冊到zk上。客戶端根據服務名從zk中找到具體的ip地址,然後根據ip地址傳送訪問。

閘道器

微服務下多個服務,對於許可權管理,流量控制,日誌,監控等和業務無關的東西提取出來,統一管理,因此就有閘道器。

閘道器可以做:鑑權,限流,請求路由,日誌,監控。

微服務

服務註冊與發現:Eureka,nacos

微服務有很多消費者,提供者,我們不希望將他們之間的呼叫寫死,因為針對某個服務,可能有多個機器去執行他,那麼我們希望有一個人能夠統一管理,那麼註冊中心可以解決我們的問題。服務提供者將自己的服務註冊到註冊中心,消費者只需根據服務名從註冊中心拿到提供者地址。並且對於閘道器,負載均衡,熔斷降級,訊息佇列等等元件,他們都希望自己能夠獲得註冊在註冊中心的某些資訊,從而進行操作。

比如監控功能,就需要拿到註冊中心的所有提供者消費者資訊,來判斷他們的健康情況。

總之,註冊中心統一對服務的管理,當我們需要使用或者檢視這些服務狀態時,只需訪問註冊中心即可。

負載均衡:Ribbon

nacos自己整合了Ribbon,要使用負載均衡,首先我們要講restTemplate通過@Bean註解註冊到spring中,然後或者使用註解,或者通過程式碼指定負載均衡策略即可,然後呼叫restTemplate.getForObject或者restTemplate.postForObject即可呼叫另一個服務的方法。

Openfeign:有人覺得直接寫restTemplate.postForObject,這樣不美觀,我就是想呼叫另一個服務就跟呼叫本地方法一樣,那麼這個Openfeign就可以幫助我們。我們寫一個介面,介面中方法指明另一個服務的介面,然後我們在controller呼叫的時候,加個註解,直接呼叫介面的方法,看起來多像在本地呼叫方法啊!

熔斷和降級:Hystrix,sentinel

當服務呼叫鏈路A->B->C,其中服務C發生故障,導致B有大量請求堆積,最終耗盡B的所有資源,B掛掉,然後A也堆積大量請求,A也掛掉,這就是服務雪崩。

為了防止服務雪崩,我們需要熔斷器,當某個服務故障時,切斷呼叫鏈路,告知上一個服務,當前服務已經故障了。

服務降級,比如某服務訪問量過大,我們一次處理不了那麼多請求,我們可以做服務降級。比如突然有1000訂單,我們一下子處理不了,那麼我們可以讓一部分請求走降級,返回稍後重試的提示資訊。

sentinel不僅僅有熔斷降級等功能,他還提供了多種其他功能,如限流,負載保護,實時監控,呼叫鏈路等。

閘道器:gateway,zuul

閘道器可以做路由對映,過濾器。

鏈路跟蹤:zipkin

每個服務是一個圓點,服務呼叫之間有連線,做出來的效果,真好看。。。?

相關文章