穩固知新-分佈式-服務註冊與發現


摘要

本文通過zk和eureka來探究分佈式系統 CAP的原則、探究分佈式系統中的服務發現不同方案的選擇,兩者有着不同的設計思路,探究不同,對比差異,方便學習,本文要求對zk和eureka有基礎的瞭解,zk的特性和功能在之前的博客裏已有描述,在此文不在贅述;

CAP原則

CAP原則指的是在一個分佈式系統中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。CAP原則是NOSQL數據庫的基石。

分佈式系統的CAP理論:理論首先把分佈式系統中的三個特性進行了如下歸納:

  • 一致性(C):在分佈式系統中的所有數據備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的數據副本)
  • 可用性(A):在集羣中一部分節點故障後,集羣整體是否還能響應客戶端的讀寫請求。(對數據更新具備高可用性)
  • 分區容忍性(P):以實際效果而言,分區相當於對通信的時限要求。系統如果不能在時限內達成數據一致性,就意味着發生了分區的情況,必須就當前操作在C和A之間做出選擇。

img

CAP三個特性只能滿足其中兩個,那麼取捨的策略就共有三種:

  • **CA without P:**如果不要求P(不允許分區),則C(強一致性)和A(可用性)是可以保證的。但放棄P的同時也就意味着放棄了系統的擴展性,也就是分佈式節點受限,沒辦法部署子節點,這是違背分佈式系統設計的初衷的。傳統的關係型數據庫RDBMS:Oracle、MySQL就是CA。

  • **CP without A:**如果不要求A(可用),相當於每個請求都需要在服務器之間保持強一致,而P(分區)會導致同步時間無限延長(也就是等待數據同步完才能正常訪問服務),一旦發生網絡故障或者消息丟失等情況,就要犧牲用戶的體驗,等待所有數據全部一致了之後再讓用戶訪問系統。設計成CP的系統其實不少,最典型的就是分佈式數據庫,如Redis、HBase等。對於這些分佈式數據庫來說,數據的一致性是最基本的要求,因爲如果連這個標準都達不到,那麼直接採用關係型數據庫就好,沒必要再浪費資源來部署分佈式數據庫。

  • **AP wihtout C:**要高可用並允許分區,則需放棄一致性。一旦分區發生,節點之間可能會失去聯繫,爲了高可用,每個節點只能用本地數據提供服務,而這樣會導致全局數據的不一致性。典型的應用就如某米的搶購手機場景,可能前幾秒你瀏覽商品的時候頁面提示是有庫存的,當你選擇完商品準備下單的時候,系統提示你下單失敗,商品已售完。這其實就是先在 A(可用性)方面保證系統可以正常的服務,然後在數據的一致性方面做了些犧牲,雖然多少會影響一些用戶體驗,但也不至於造成用戶購物流程的嚴重阻塞。

根據上面的分析,也就是說在分佈式存儲系統中,最多隻能實現上面的兩點。而由於網絡硬件肯定會出現延遲丟包等問題,所以分區容錯性是我們必須需要實現的,所以也就剩下CP、AP的方案;概念已經分析完畢,今天主要以zk和eureka爲例探究分佈式系統中的服務發現不同方案的選擇。

分佈式系統-服務發現

在分佈式系統中,我們需要將自己提供的服務暴露給使用方,dubbo、springcloud是目前在國內相對比較流暢兩套爲服務架構,dubbo使用zk作爲服務發現、springcloud使用eureka作爲服務發現,但二者又較大區別;

image-20200614220607746

Zookeeper遵守CP

zookeeper是一種提供強一致性的服務。網絡上大多數人描述zookeeper的缺點:當向註冊中心查詢服務列表時,我們可以容忍註冊中心返回的是幾分鐘以前的註冊信息,但不能接受服務直接down掉不可用。也就是說,服務註冊功能對可用性的要求高於一致性。但是zk會出現這一種情況,當master節點因爲網絡故障與其他節點失去聯繫時,剩餘註冊功能就會重新進行leader選舉看。問題在於,選舉leader的時間太長,30~120s,且選舉期間整個zk集羣都是不可用的,這就導致在選舉期間註冊服務癱瘓。在雲部署的環境下,因網絡問題使得zk集羣失去master節點是較大概率會發生的事,雖然服務能夠最終恢復,但是漫長的選舉時間導致的註冊長期不可用是不能容忍的;

我個人覺得zk的優勢在於,zk採用訂閱的方式,如果服務節點有變化,zkclient能很快感知到,實時性更高一些,針對zookeeper的缺點,dubbo也做了針對性優化,比如zk真的掛掉了,不影響服務直接的相互調用,在我看來針對性的優化還是很有優勢的。

Eureka遵守AP

Eureka組件

包含兩個組件:Eureka Server和Eureka Client。

  • Eureka Server 提供服務註冊服務,各個節點啓動後會在 Eureka Server 中進行註冊,這樣 EurekaServer 中的服務註冊表中將會存儲所有可用服務節點的信息,服務節點的信息可以在界面中直觀的看到。
  • Eureka Client 是一個java客戶端,用於簡化與 Eureka Server 的交互,客戶端同時也就是一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。

img

Feature:

  • Eureka 不持久化緩存,重啓後內存數據丟失;
  • Eureka 通過增量更新註冊信息,只關心瞬時狀態;
  • Eureka 提供客戶端緩存,寧可返回某服務5分鐘之前可用的服務器列表信息,也不能因爲暫時的網絡故障而找不到可用的服務器,滿足 CAP 中的 AP。

服務啓動後向Eureka註冊,Eureka Server會將註冊信息向其他Eureka Server進行同步,當服務消費者要調用服務提供者,則向服務註冊中心獲取服務提供者地址,然後會將服務提供者地址緩存在本地,下次再調用時,則直接從本地緩存中取,完成一次調用。

當服務註冊中心Eureka Server檢測到服務提供者因爲宕機、網絡原因不可用時,則在服務註冊中心將服務置爲DOWN狀態,並把當前服務提供者狀態向訂閱者發佈,訂閱過的服務消費者更新本地緩存。

服務提供者在啓動後,週期性(默認30秒)向Eureka Server發送心跳,以證明當前服務是可用狀態。Eureka Server在一定的時間(默認90秒)未收到客戶端的心跳,則認爲服務宕機,註銷該實例。

Eureka緩存

Eureka Server 存在三個變量(三級緩存機制):

  • registry(ConcurrentHashMap):實時更新,類 AbstractInstanceRegistry 成員變量,UI 端請求的是這裏的服務註冊信息。
  • readWriteCacheMap(Guava Cache/LoadingCache):實時更新,類 ResponseCacheImpl 成員變量,緩存時間 180 秒。
  • readOnlyCacheMap(ConcurrentHashMap):週期更新,類 ResponseCacheImpl 成員變量,默認每30s從 readWriteCacheMap 更新,Eureka client 默認從這裏更新服務註冊信息,可配置直接從 readWriteCacheMap 更新

img

Eureka Server 通過上面的三個變量來保存服務註冊信息。默認情況下定時任務每 30s 將 readWriteCacheMap 同步至 readOnlyCacheMap,每 60s 清理超過 90s 未續約的節點,Eureka Client 每 30s 從 readOnlyCacheMap 更新服務註冊信息,而 UI 則從 registry 更新服務註冊信息;

Eureka Server 通過上面的三個變量來保存服務註冊信息。默認情況下定時任務每 30s 將 readWriteCacheMap 同步至 readOnlyCacheMap,每 60s 清理超過 90s 未續約的節點,Eureka Client 每 30s 從 readOnlyCacheMap 更新服務註冊信息,而 UI 則從 registry 更新服務註冊信息。

瞭解了上面的緩存原理,Eureka的缺點也就很明顯了,實時性不夠,但省去了zookeeper的中途選舉leader時服務不可用的問題,對比起來似乎有一些優勢;

可以參考一下網絡上的配置

Server Configuration

## 中小規模下,自我保護模式坑比好處多,所以關閉它
eureka.server.enableSelfPreservation=false
## 心跳閾值計算週期,如果開啓自我保護模式,可以改一下這個配置
## eureka.server.renewalThresholdUpdateIntervalMs=120000
## 主動失效檢測間隔,配置成5秒
eureka.server.evictionIntervalTimerInMs=5000
## 心跳間隔,5秒
eureka.instance.leaseRenewalIntervalInSeconds=5
## 沒有心跳的淘汰時間,10秒
eureka.instance.leaseExpirationDurationInSeconds=10
## 禁用readOnlyCacheMap
eureka.server.useReadOnlyResponseCache=false

Client Configuration

## 心跳間隔,5秒
eureka.instance.leaseRenewalIntervalInSeconds=5
## 沒有心跳的淘汰時間,10秒
eureka.instance.leaseExpirationDurationInSeconds=10
# 定時刷新本地緩存時間
eureka.client.registryFetchIntervalSeconds=5
# ribbon緩存時間
ribbon.ServerListRefreshInterval=2000

經過調優後,eureka也能到達秒級;

其它

當然市面上還有其它的其它的方案,比如阿里巴巴nacos等,新的技術出現、流行一定是爲了彌補現在出現的問題,當然公司在選擇切換的時候也有一定成本,如果作爲公司的架構師、技術專家,那麼一定也會考慮更多、權衡利弊之後纔會做出抉擇;nacos並不在本文討論的範圍之內,可以根據參考鏈接的《Nacos與Eureka區別及如何選型》做對比。

參考

CAP原則(CAP定理)、BASE理論

深入學習 Eureka 原理

Nacos與Eureka區別及如何選型


你的鼓勵也是我創作的動力

打賞地址

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