經濟依舊不景氣啊,如此大環境下Java還是這么卷,又是一年一次的金三銀四。
兄弟們,你準備好了嗎?沖沖沖!歐里給!
分布式/微服務相關面試題解
- 題一:CAP理論,BASE理論
- 題二:負載均衡算法、類型有哪些
- 算法
- 1、輪詢法
- 2、隨機法
- 3、源地址哈希法
- 4、加權輪詢法
- 5、加權隨機法
- 6、最小連接數法
- 類型
- DNS 方式實現負載均衡
- 硬件負載均衡:F5 和 A10
- 軟件負載均衡(Nginx 、 HAproxy 、 LVS )
- 題三:分布式架構下,Session 共享有什么方案?
- 題四:簡述你對RPC、RMI的理解
- 題五:分布式id生成方案
- UUID
- 數據庫自增序列
- Leaf-segment
- 基于redis、mongodb、zk等中間件生成
- 雪花算法
- 題六:分布式鎖解決方案
- Zookeeper分布式鎖
- Redis分布式鎖
- 題七:分布式事務解決方案
- 題八:如何實現接口的冪等性?
- 題九:簡述ZAB 協議
- 題十:zk的數據模型和節點類型?
- 題十一:簡述zk的命名服務、配置管理、集群管理
- 題十二:講下Zookeeper watch機制
- 題十三:zk和eureka的區別?
- 題十四:Spring Cloud和Dubbo的區別?
- 題十五:什么是Hystrix?簡述實現機制
- 題十六:springcloud核心組件及其作用
- 題十七:Dubbo 的整體架構設計及分層
題一:CAP理論,BASE理論
Consistency (一致性):
即更新操作成功并返回客戶端后,所有節點在同一時間的數據完全一致。
對于客戶端來說,一致性指的是并發訪問時更新過的數據如何獲取的問題。
從服務端來看,則是更新如何復制分布到整個系統,以保證數據最終一致。
Availability (可用性):
即服務一直可用,而且是正常響應時間。系統能夠很好的為用戶服務,不出現用戶操作失敗或者訪問超時等用戶體驗不好的情況。
Partition Tolerance (分區容錯性):
即分布式系統在遇到某節點或網絡分區故障的時候,仍然能夠對外提供滿足一致性和可用性的服務。分區容錯性要求能夠使應用雖然是一個分布式系統,而看上去卻好像是在一個可以運轉正常的整體。比如現在的分布式系統中有某一個或者幾個機器宕掉了,其他剩下的機器還能夠正常運轉滿足系統需求,對于用戶而言并沒有什么體驗上的影響。
CP和AP:分區容錯是必須保證的,當發生網絡分區的時候,如果要繼續服務,那么強一致性和可用性只能 2 選 1
BASE是Basically Available(基本可用)、Soft state(軟狀態)和Eventually consistent(最終一致性)
BASE理論是對CAP中一致性和可用性權衡的結果,其來源于對大規模互聯網系統分布式實踐的總結,是基于CAP定理逐步演化而來的。BASE理論的核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,采用適當的方式來使系統達到最終一致性。
基本可用:
- 響應時間上的損失: 正常情況下,處理用戶請求需要 0.5s 返回結果,但是由于系統出現故障,處理用戶請求的時間變為 3 s。
- 系統功能上的損失:正常情況下,用戶可以使用系統的全部功能,但是由于系統訪問量突然劇增,系統的部分非核心功能無法使用。
軟狀態:數據同步允許一定的延遲
最終一致性:系統中所有的數據副本,在經過一段時間的同步后,最終能夠達到一個一致的狀態,不要求實時
題二:負載均衡算法、類型有哪些
算法
1、輪詢法
將請求按順序輪流地分配到后端服務器上,它均衡地對待后端的每一臺服務器,而不關心服務器實際的連接數和當前的系統負載。
2、隨機法
通過系統的隨機算法,根據后端服務器的列表大小值來隨機選取其中的一臺服務器進行訪問。由概率統計理論可以得知,隨著客戶端調用服務端的次數增多,其實際效果越來越接近于平均分配調用量到后端的每一臺服務器,也就是輪詢的結果。
3、源地址哈希法
源地址哈希的思想是根據獲取客戶端的IP地址,通過哈希函數計算得到的一個數值,用該數值對服務器列表的大小進行取模運算,得到的結果便是客服端要訪問服務器的序號。采用源地址哈希法進行負載均衡,同一IP地址的客戶端,當后端服務器列表不變時,它每次都會映射到同一臺后端服務器進行訪問。
4、加權輪詢法
不同的后端服務器可能機器的配置和當前系統的負載并不相同,因此它們的抗壓能力也不相同。給配置高、負載低的機器配置更高的權重,讓其處理更多的請求;而配置低、負載高的機器,給其分配較低的權重,降低其系統負載,加權輪詢能很好地處理這一問題,并將請求順序且按照權重分配到后端。
5、加權隨機法
與加權輪詢法一樣,加權隨機法也根據后端機器的配置,系統的負載分配不同的權重。不同的是,它是按照權重隨機請求后端服務器,而非順序。
6、最小連接數法
最小連接數算法比較靈活和智能,由于后端服務器的配置不盡相同,對于請求的處理有快有慢,它是根據后端服務器當前的連接情況,動態地選取其中當前積壓連接數最少的一臺服務器來處理當前的請求,盡可能地提高后端服務的利用效率,將負責合理地分流到每一臺服務器。
類型
DNS 方式實現負載均衡
硬件負載均衡:F5 和 A10
軟件負載均衡(Nginx 、 HAproxy 、 LVS )
區別:
- Nginx :七層負載均衡,支持 HTTP、E-mail 協議,同時也支持 4 層負載均衡;
- HAproxy :支持七層規則的,性能也很不錯。OpenStack 默認使用的負載均衡軟件就是HAproxy;
- LVS :運行在內核態,性能是軟件負載均衡中最高的,嚴格來說工作在三層,所以更通用一些,適用各種應用服務。
題三:分布式架構下,Session 共享有什么方案?
1、采用無狀態服務,拋棄session
2、存入cookie(有安全風險)
3、服務器之間進行 Session 同步,這樣可以保證每個服務器上都有全部的 Session 信息,不過當服務器數量比較多的時候,同步是會有延遲甚至同步失敗;
4、 IP 綁定策略使用 Nginx (或其他復雜均衡軟硬件)中的 IP 綁定策略,同一個 IP 只能在指定的同一個機器訪問,但是這樣做失去了負載均衡的意義,當掛掉一臺服務器的時候,會影響一批用戶的使用,風險很大;
5、使用 Redis 存儲把 Session 放到 Redis 中存儲,雖然架構上變得復雜,并且需要多訪問一次 Redis ,但是這種方案帶來的好處也是很大的:
- 實現了 Session 共享;
- 可以水平擴展(增加 Redis 服務器);
- 服務器重啟 Session 不丟失(不過也要注意 Session 在 Redis 中的刷新/失效機制);
- 不僅可以跨服務器 Session 共享,甚至可以跨平臺(例如網頁端和 APP 端)。
題四:簡述你對RPC、RMI的理解
RPC:在本地調用遠程的函數,遠程過程調用,可以跨語言實現 httpClient
RMI:遠程方法調用,java中用于實現RPC的一種機制,RPC的java版本,是J2EE的網絡調用機制,跨JVM調用對象的方法,面向對象的思維方式
直接或間接實現接口 java.rmi.Remote 成為存在于服務器端的遠程對象,供客戶端訪問并提供一定的服務
遠程對象必須實現java.rmi.server.UniCastRemoteObject類,這樣才能保證客戶端訪問獲得遠程對象
時,該遠程對象將會把自身的一個拷貝以Socket的形式傳輸給客戶端,此時客戶端所獲得的這個拷貝稱
為“存根”,而服務器端本身已存在的遠程對象則稱之為“骨架”。其實此時的存根是客戶端的一個代理,
用于與服務器端的通信,而骨架也可認為是服務器端的一個代理,用于接收客戶端的請求之后調用遠程
方法來響應客戶端的請求。
public interface IService extends Remote {String service(String content) throws RemoteException;
}
public class ServiceImpl extends UnicastRemoteObject implements IService {private String name;public ServiceImpl(String name) throws RemoteException {this.name = name;}@Overridepublic String service(String content) {return "server >> " + content;}}
public class Server {public static void main(String[] args) {try {IService service02 = new ServiceImpl("service02");Context namingContext = new InitialContext();namingContext.rebind("rmi://127.0.0.1/service02", service02);} catch (Exception e) {e.printStackTrace();}System.out.println("000000!");}
}
public class Client {public static void main(String[] args) {String url = "rmi://127.0.0.1/";try {Context namingContext = new InitialContext();IService service02 = (IService) namingContext.lookup(url +
"service02");Class stubClass = service02.getClass();System.out.println(service02 + " is " + stubClass.getName());//com.sun.proxy.$Proxy0Class[] interfaces = stubClass.getInterfaces();for (Class c : interfaces) {System.out.println("implement" + c.getName() + " interface");}System.out.println(service02.service("hello"));} catch (Exception e) {e.printStackTrace();}}
}
題五:分布式id生成方案
UUID
1,當前日期和時間 ---->時間戳
2,時鐘序列。 ---->計數器
3,全局唯一的IEEE機器識別號,如果有網卡,從網卡MAC地址獲得,沒有網卡以其他方式獲得。
優點:
- 代碼簡單,性能好(本地生成,沒有網絡消耗)
- 保證唯一(相對而言,重復概率極低可以忽略)
缺點:
- 每次生成的ID都是無序的,而且不是全數字,且無法保證趨勢遞增。
- UUID生成的是字符串,字符串存儲性能差,查詢效率慢,寫的時候由于不能產生順序的append 操作,需要進行insert操作,導致頻繁的頁分裂,這種操作在記錄占用空間比較大的情況下,性能下降比較大,還會增加讀取磁盤次數
- UUID長度過長,不適用于存儲,耗費數據庫性能。
- ID無一定業務含義,可讀性差。
- 有信息安全問題,有可能泄露mac地址
數據庫自增序列
單機模式:
優點:
- 實現簡單,依靠數據庫即可,成本小。
- ID數字化,單調自增,滿足數據庫存儲和查詢性能。
- 具有一定的業務可讀性。(結合業務code)
缺點:
- 強依賴DB,存在單點問題,如果數據庫宕機,則業務不可用。
- DB生成ID性能有限,單點數據庫壓力大,無法扛高并發場景。
- 信息安全問題,比如暴露訂單量,url查詢改一下id查到別人的訂單
數據庫高可用:多主模式做負載,基于序列的起始值和步長設置,不同的初始值,相同的步長,步長大于節點數
優點:
- 解決了ID生成的單點問題,同時平衡了負載。
缺點:
- 系統擴容困難:系統定義好步長之后,增加機器之后調整步長困難。
- 數據庫壓力大:每次獲取一個ID都必須讀寫一次數據庫。
- 主從同步的時候:電商下單->支付insert master db select數據,因為數據同步延遲導致 查不到這個數據。加cache(不是最好的解決方式)數據要求比較嚴謹的話查master主庫。
Leaf-segment
采用每次獲取一個ID區間段的方式來解決,區間段用完之后再去數據庫獲取新的號段,這樣一來可以大大減輕數據庫的壓力
核心字段:biz_tag,max_id,step
biz_tag用來區分業務,max_id表示該biz_tag目前所被分配的ID號段的最大值,step表示每次分配的號段長度,原來每次獲取ID都要訪問數據庫,現在只需要把Step設置的足夠合理如1000,那么現在可以在1000個ID用完之后再去訪問數據庫。
優點:
- 擴張靈活,性能強能夠撐起大部分業務場景。
- ID號碼是趨勢遞增的,滿足數據庫存儲和查詢性能要求。
- 可用性高,即使ID生成服務器不可用,也能夠使得業務在短時間內可用,為排查問題爭取時間。
缺點:
- 可能存在多個節點同時請求ID區間的情況,依賴DB
雙buffer:將獲取一個號段的方式優化成獲取兩個號段,在一個號段用完之后不用立馬去更新號段,還有一個緩存號段備用,這樣能夠有效解決這種沖突問題,而且采用雙buffer的方式,在當前號段消耗了10%的時候就去檢查下一個號段有沒有準備好,如果沒有準備好就去更新下一個號段,當當前號段用完了就切換到下一個已經緩存好的號段去使用,同時在下一個號段消耗到10%的時候,又去檢測下一個號段有沒有準備好,如此往復。
優點:
- 基于JVM存儲雙buffer的號段,減少了數據庫查詢,減少了網絡依賴,效率更高。
缺點:
- segment號段長度是固定的,業務量大時可能會頻繁更新號段,因為原本分配的號段會一下用完
- 如果號段長度設置的過長,但凡緩存中有號段沒有消耗完,其他節點重新獲取的號段與之前相比可能跨度會很大。動態調整Step
基于redis、mongodb、zk等中間件生成
雪花算法
生成一個64bit的整性數字
第一位符號位固定為0,41位時間戳,10位workId,12位序列號
位數可以有不同實現
優點:
- 每個毫秒值包含的ID值很多,不夠可以變動位數來增加,性能佳(依賴workId的實現)。
- 時間戳值在高位,中間是固定的機器碼,自增的序列在低位,整個ID是趨勢遞增的。 能夠根據業務場景數據庫節點布置靈活挑戰bit位劃分,靈活度高。
缺點:
- 強依賴于機器時鐘,如果時鐘回撥,會導致重復的ID生成,所以一般基于此的算法發現時鐘回撥,都會拋異常處理,阻止ID生成,這可能導致服務不可用。
題六:分布式鎖解決方案
需要這個鎖獨立于每一個服務之外,而不是在服務里面。
數據庫:利用主鍵沖突控制一次只有一個線程能獲取鎖,非阻塞、不可重入、單點、失效時間
Zookeeper分布式鎖
zk通過臨時節點,解決了死鎖的問題,一旦客戶端獲取到鎖之后突然掛掉(Session連接斷開),那么這個臨時節點就會自動刪除掉,其他客戶端自動獲取鎖。臨時順序節點解決驚群效應
Redis分布式鎖
setNX,單線程處理網絡請求,不需要考慮并發安全性
所有服務節點設置相同的key,返回為0、則鎖獲取失敗
setnx 問題:
1、早期版本沒有超時參數,需要單獨設置,存在死鎖問題(中途宕機)
2、后期版本提供加鎖與設置時間原子操作,但是存在任務超時,鎖自動釋放,導致并發問題,加鎖與釋放鎖不是同一線程問題
刪除鎖:判斷線程唯一標志,再刪除
可重入性及鎖續期沒有實現,通過redisson解決(類似AQS的實現,看門狗監聽機制)
redlock:意思的機制都只操作單節點、即使Redis通過sentinel保證高可用,如果這個master節點由于某些原因發生了主從切換,那么就會出現鎖丟失的情況(redis同步設置可能數據丟失)。redlock從多個節點申請鎖,當一半以上節點獲取成功、鎖才算獲取成功,redission有相應的實現。
題七:分布式事務解決方案
XA規范:分布式事務規范,定義了分布式事務模型
四個角色:事務管理器(協調者TM)、資源管理器(參與者RM),應用程序AP,通信資源管理器CRM
全局事務:一個橫跨多個數據庫的事務,要么全部提交、要么全部回滾
JTA事務是java對XA規范的實現,對應JDBC的單庫事務
兩階段協議:
第一階段( prepare ) :每個參與者執行本地事務但不提交,進入 ready 狀態,并通知協調者已經準備就緒。
第二階段( commit ) 當協調者確認每個參與者都 ready 后,通知參與者進行 commit 操作;如果有參與者 fail ,則發送 rollback 命令,各參與者做回滾。
問題:
- 單點故障:一旦事務管理器出現故障,整個系統不可用(參與者都會阻塞住)
- 數據不一致:在階段二,如果事務管理器只發送了部分 commit 消息,此時網絡發生異常,那么只有部分參與者接收到 commit 消息,也就是說只有部分參與者提交了事務,使得系統數據不一致。
- 響應時間較長:參與者和協調者資源都被鎖住,提交或者回滾之后才能釋放
- 不確定性:當協調者事務管理器發送 commit 之后,并且此時只有一個參與者收到了 commit,那么當該參與者與事務管理器同時宕機之后,重新選舉的事務管理器無法確定該條消息是否提交成功。
三階段協議:主要是針對兩階段的優化,解決了2PC單點故障的問題,但是性能問題和不一致問題仍然沒有根本解決
引入了超時機制解決參與者阻塞的問題,超時后本地提交,2pc只有協調者有超時機制
-
第一階段:CanCommit階段,協調者詢問事務參與者,是否有能力完成此次事務。
- 如果都返回yes,則進入第二階段
- 有一個返回no或等待響應超時,則中斷事務,并向所有參與者發送abort請求
-
第二階段:PreCommit階段,此時協調者會向所有的參與者發送PreCommit請求,參與者收到后開始執行事務操作。參與者執行完事務操作后(此時屬于未提交事務的狀態),就會向協調者反饋“Ack”表示我已經準備好提交了,并等待協調者的下一步指令。
-
第三階段:DoCommit階段, 在階段二中如果所有的參與者節點都返回了Ack,那么協調者就會從“預提交狀態”轉變為“提交狀態”。然后向所有的參與者節點發送"doCommit"請求,參與者節點在收到提交請求后就會各自執行事務提交操作,并向協調者節點反饋“Ack”消息,協調者收到所有參與者的Ack消息后完成事務。 相反,如果有一個參與者節點未完成PreCommit的反饋或者反饋超時,那么協調者都會向所有的參與者節點發送abort請求,從而中斷事務。
TCC(補償事務):Try、Confirm、Cancel
針對每個操作,都要注冊一個與其對應的確認和補償(撤銷)操作
Try操作做業務檢查及資源預留,Confirm做業務確認操作,Cancel實現一個與Try相反的操作既回滾操作。TM首先發起所有的分支事務的try操作,任何一個分支事務的try操作執行失敗,TM將會發起所有分支事務的Cancel操作,若try操作全部成功,TM將會發起所有分支事務的Confirm操作,其中Confirm/Cancel操作若執行失敗,TM會進行重試。
TCC模型對業務的侵入性較強,改造的難度較大,每個操作都需要有 try 、 confirm 、cancel 三個接口實現。
confirm 和 cancel 接口還必須實現冪等性。
消息隊列的事務消息:
- 發送prepare消息到消息中間件
- 發送成功后,執行本地事務
- 如果事務執行成功,則commit,消息中間件將消息下發至消費端(commit前,消息不會被消費)
- 如果事務執行失敗,則回滾,消息中間件將這條prepare消息刪除
- 消費端接收到消息進行消費,如果消費失敗,則不斷重試
題八:如何實現接口的冪等性?
- 唯一id。每次操作,都根據操作和內容生成唯一的id,在執行之前先判斷id是否存在,如果不存在則執行后續操作,并且保存到數據庫或者redis等。
- 服務端提供發送token的接口,業務調用接口前先獲取token,然后調用業務接口請求時,把token攜帶過去,服務器判斷token是否存在redis中,存在表示第一次請求,可以繼續執行業務,執行業務完成后,最后需要把redis中的token刪除
- 建去重表。將業務中有唯一標識的字段保存到去重表,如果表中存在,則表示已經處理過了
- 版本控制。增加版本號,當版本號符合時,才能更新數據
- 狀態控制。例如訂單有狀態已支付 未支付 支付中 支付失敗,當處于未支付的時候才允許修改為支付中。
。。。。。。
題九:簡述ZAB 協議
ZAB 協議是為分布式協調服務 Zookeeper 專門設計的一種支持崩潰恢復的原子廣播協議,實現分布式數據一致性
所有客戶端的請求都是寫入到 Leader 進程中,然后,由 Leader 同步到其他節點,稱為 Follower。在集群數據同步的過程中,如果出現 Follower 節點崩潰或者 Leader 進程崩潰時,都會通過 Zab 協議來保證數據一致性
ZAB 協議包括兩種基本的模式:崩潰恢復和消息廣播。
消息廣播:
集群中所有的事務請求都由 Leader 節點來處理,其他服務器為 Follower,Leader 將客戶端的事務請求轉換為事務 Proposal,并且將 Proposal 分發給集群中其他所有的 Follower。
完成廣播之后,Leader 等待 Follwer 反饋,當有過半數的 Follower 反饋信息后,Leader 將再次向集群內 Follower 廣播 Commit 信息,Commit 信息就是確認將之前的 Proposal 提交。
Leader 節點的寫入是一個兩步操作,第一步是廣播事務操作,第二步是廣播提交操作,其中過半數指的是反饋的節點數 >=N/2+1,N 是全部的 Follower 節點數量。
崩潰恢復:
- 初始化集群,剛剛啟動的時候
- Leader 崩潰,因為故障宕機
- Leader 失去了半數的機器支持,與集群中超過一半的節點斷連
此時開啟新一輪 Leader 選舉,選舉產生的 Leader 會與過半的 Follower 進行同步,使數據一致,當與過半的機器同步完成后,就退出恢復模式, 然后進入消息廣播模式
整個 ZooKeeper 集群的一致性保證就是在上面兩個狀態之前切換,當 Leader 服務正常時,就是正常的消息廣播模式;當 Leader 不可用時,則進入崩潰恢復模式,崩潰恢復階段會進行數據同步,完成以后,重新進入消息廣播階段。
Zxid 是 Zab 協議的一個事務編號,Zxid 是一個 64 位的數字,其中低 32 位是一個簡單的單調遞增計數器,針對客戶端每一個事務請求,計數器加 1;而高 32 位則代表 Leader 周期年代的編號。
Leader 周期( epoch),可以理解為當前集群所處的年代或者周期,每當有一個新的Leader 選舉出現時,就會從這個 Leader 服務器上取出其本地日志中最大事務的 Zxid,并從中讀取 epoch 值,然后加 1,以此作為新的周期 ID。高 32 位代表了每代 Leader 的唯一性,低 32 位則代表了每代 Leader 中事務的唯一性。
zab節點的三種狀態:
following:服從leader的命令
leading:負責協調事務
election/looking:選舉狀態
題十:zk的數據模型和節點類型?
數據模型:樹形結構
zk維護的數據主要有:客戶端的會話(session)狀態及數據節點(dataNode)信息。
zk在內存中構造了個DataTree的數據結構,維護著path到dataNode的映射以及dataNode間的樹狀層級關系。為了提高讀取性能,集群中每個服務節點都是將數據全量存儲在內存中。所以,zk最適于讀多寫少且輕量級數據的應用場景。
數據僅存儲在內存是很不安全的,zk采用事務日志文件及快照文件的方案來落盤數據,保障數據在不丟失的情況下能快速恢復。
樹中的每個節點被稱為— Znode
Znode 兼具文件和目錄兩種特點。可以做路徑標識,也可以存儲數據,并可以具有子 Znode。具有增、刪、改、查等操作。
Znode 具有原子性操作,讀操作將獲取與節點相關的所有數據,寫操作也將替換掉節點的所有數據。另外,每一個節點都擁有自己的 ACL(訪問控制列表),這個列表規定了用戶的權限,即限定了特定用戶對目標節點可以執行的操作。
Znode 存儲數據大小有限制。每個 Znode 的數據大小至多 1M,常規使用中應該遠小于此值。
Znode 通過路徑引用,如同 Unix 中的文件路徑。路徑必須是絕對的,因此他們必須由斜杠字符來開頭。除此以外,他們必須是唯一的,也就是說每一個路徑只有一個表示,因此這些路徑不能改變。在ZooKeeper 中,路徑由 Unicode 字符串組成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如關鍵配額信息。
持久節點:一旦創建、該數據節點會一直存儲在zk服務器上、即使創建該節點的客戶端與服務端的會話關閉了、該節點也不會被刪除
臨時節點:當創建該節點的客戶端會話因超時或發生異常而關閉時、該節點也相應的在zk上被刪除 。
有序節點:不是一種單獨種類的節點、而是在持久節點和臨時節點的基礎上、增加了一個節點有序的性質 。
題十一:簡述zk的命名服務、配置管理、集群管理
命名服務:
通過指定的名字來獲取資源或者服務地址。Zookeeper可以創建一個全局唯一的路徑,這個路徑就可以作為一個名字。被命名的實體可以是集群中的機器,服務的地址,或者是遠程的對象等。一些分布式服務框架(RPC、RMI)中的服務地址列表,通過使用命名服務,客戶端應用能夠根據特定的名字來獲取資源的實體、服務地址和提供者信息等
配置管理:
實際項目開發中,經常使用.properties或者xml需要配置很多信息,如數據庫連接信息、fps地址端口等等。程序分布式部署時,如果把程序的這些配置信息保存在zk的znode節點下,當你要修改配置,即znode會發生變化時,可以通過改變zk中某個目錄節點的內容,利用watcher通知給各個客戶端,從而更改配置。
集群管理:
集群管理包括集群監控和集群控制,就是監控集群機器狀態,剔除機器和加入機器。zookeeper可以方便集群機器的管理,它可以實時監控znode節點的變化,一旦發現有機器掛了,該機器就會與zk斷開連接,對應的臨時目錄節點會被刪除,其他所有機器都收到通知。新機器加入也是類似。
題十二:講下Zookeeper watch機制
客戶端,可以通過在znode上設置watch,實現實時監聽znode的變化
Watch事件是一個一次性的觸發器,當被設置了Watch的數據發生了改變的時候,則服務器將這個改變發送給設置了Watch的客戶端
- 父節點的創建,修改,刪除都會觸發Watcher事件。
- 子節點的創建,刪除會觸發Watcher事件。
一次性:一旦被觸發就會移除,再次使用需要重新注冊,因為每次變動都需要通知所有客戶端,一次性可以減輕壓力,3.6.0默認持久遞歸,可以觸發多次
輕量:只通知發生了事件,不會告知事件內容,減輕服務器和帶寬壓力
Watcher 機制包括三個角色:客戶端線程、客戶端的 WatchManager 以及 ZooKeeper 服務器
- 客戶端向 ZooKeeper 服務器注冊一個 Watcher 監聽,
- 把這個監聽信息存儲到客戶端的 WatchManager 中
- 當 ZooKeeper 中的節點發生變化時,會通知客戶端,客戶端會調用相應 Watcher 對象中的回調方法。watch回調是串行同步的
題十三:zk和eureka的區別?
zk:CP設計(強一致性),目標是一個分布式的協調系統,用于進行資源的統一管理。
當節點crash后,需要進行leader的選舉,在這個期間內,zk服務是不可用的。
eureka:AP設計(高可用),目標是一個服務注冊發現系統,專門用于微服務的服務發現注冊。
Eureka各個節點都是平等的,幾個節點掛掉不會影響正常節點的工作,剩余的節點依然可以提供注冊和查詢服務。而Eureka的客戶端在向某個Eureka注冊時如果發現連接失敗,會自動切換至其他節點,只要有一臺Eureka還在,就能保證注冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)
同時當eureka的服務端發現85%以上的服務都沒有心跳的話,它就會認為自己的網絡出了問題,就不會從服務列表中刪除這些失去心跳的服務,同時eureka的客戶端也會緩存服務信息。eureka對于服務注冊發現來說是非常好的選擇。
題十四:Spring Cloud和Dubbo的區別?
底層協議:springcloud基于http協議,dubbo基于Tcp協議,決定了dubbo的性能相對會比較好。
注冊中心:Spring Cloud 使用的 eureka ,dubbo推薦使用zookeeper
模型定義:dubbo 將一個接口定義為一個服務,SpringCloud 則是將一個應用定義為一個服務
SpringCloud是一個生態,而Dubbo是SpringCloud生態中關于服務調用一種解決方案(服務治理)
題十五:什么是Hystrix?簡述實現機制
分布式容錯框架
- 阻止故障的連鎖反應,實現熔斷
- 快速失敗,實現優雅降級
- 提供實時的監控和告警
資源隔離:線程隔離,信號量隔離
-
線程隔離:Hystrix會給每一個Command分配一個單獨的線程池,這樣在進行單個服務調用的時候,就可以在獨立的線程池里面進行,而不會對其他線程池造成影響
-
信號量隔離:客戶端需向依賴服務發起請求時,首先要獲取一個信號量才能真正發起調用,由于信號量的數量有限,當并發請求量超過信號量個數時,后續的請求都會直接拒絕,進入fallback流程。信號量隔離主要是通過控制并發請求量,防止請求線程大面積阻塞,從而達到限流和防止雪崩的目的。
熔斷和降級:調用服務失敗后快速失敗
熔斷是為了防止異常不擴散,保證系統的穩定性
降級:編寫好調用失敗的補救邏輯,然后對服務直接停止運行,這樣這些接口就無法正常調用,但又不至于直接報錯,只是服務水平下降
- 通過HystrixCommand 或者HystrixObservableCommand 將所有的外部系統(或者稱為依賴)包裝起來,整個包裝對象是單獨運行在一個線程之中(這是典型的命令模式)。
- 超時請求應該超過你定義的閾值
- 為每個依賴關系維護一個小的線程池(或信號量); 如果它變滿了,那么依賴關系的請求將立即被拒絕,而不是排隊等待。
- 統計成功,失敗(由客戶端拋出的異常),超時和線程拒絕。
- 打開斷路器可以在一段時間內停止對特定服務的所有請求,如果服務的錯誤百分比通過閾值,手動或自動的關閉斷路器。
- 當請求被拒絕、連接超時或者斷路器打開,直接執行fallback邏輯。
- 近乎實時監控指標和配置變化。
題十六:springcloud核心組件及其作用
Eureka:服務注冊與發現
注冊:每個服務都向Eureka登記自己提供服務的元數據,包括服務的ip地址、端口號、版本號、通信協議等。eureka將各個服務維護在了一個服務清單中(雙層Map,第一層key是服務名,第二層key是實例名,value是服務地址加端口)。同時對服務維持心跳,剔除不可用的服務,eureka集群各節點相互注冊每個實例中都有一樣的服務清單。
發現:eureka注冊的服務之間調用不需要指定服務地址,而是通過服務名向注冊中心咨詢,并獲取所有服務實例清單(緩存到本地),然后實現服務的請求訪問。
Ribbon:服務間發起請求的時候,基于Ribbon做負載均衡,從?個服務的多臺機器中選擇?臺 (被調用方的服務地址有多個),Ribbon也是通過發起http請求,來進行的調用,只不過是通過調用服務名的地址來實現的。雖然說Ribbon不用去具體請求服務實例的ip地址或域名了,但是每調用一個接口都還要手動去發起Http請求。
@RestController
public class ConsumerController {@AutowiredRestTemplate restTemplate;@GetMapping("/ribbon-consumer")public String helloConsumer(){return
restTemplate.getForEntity("http://exampleservice/index",String.class).getBody();}
}
Feign:基于Feign的動態代理機制,根據注解和選擇的機器,拼接請求URL地址,發起請求 ,簡化服務間的調用,在Ribbon的基礎上進行了進一步的封裝。單獨抽出了一個組件,就是Spring Cloud Feign。
在引入Spring Cloud Feign后,我們只需要創建一個接口并用注解的方式來配置它,即可完成對服務提供方的接口綁定。
調用遠程就像調用本地服務一樣
UserController.java
@RestController
public class UserController {@GetMapping("/getUser")public String getUser(){List<String> list = new ArrayList<>();list.add("張三");String json = JSON.toJSONString(list);return json;}
}
UserClient.java
@FeignClient(name = "user")
public interface UserClient {@GetMapping("/getUser")String getUser();
}
TestController.java
@RestController
public class TestController {@ResourceUserClient userClient;@RequestMapping("/test")public String test(){String user = userClient.getUser();return user;}
}
Hystrix:發起請求是通過Hystrix的線程池來走的,不同的服務走不同的線程池,實現了不同服務調用的隔離,通過統計接口超時次數返回默認值,實現服務熔斷和降級。
Zuul:如果前端、移動端要調用后端系統,統?從Zuul網關進入,由Zuul網關轉發請求給對應的服務,通過與Eureka進行整合,將自身注冊為Eureka下的應用,從Eureka下獲取所有服務的實例,來進行服務的路由。Zuul還提供了一套過濾器機制,開發者可以自己指定哪些規則的請求需要執行校驗邏輯,只有通過校驗邏輯的請求才會被路由到具體服務實例上,否則返回錯誤提示。
題十七:Dubbo 的整體架構設計及分層
五個角色:
- 注冊中心registry:服務注冊與發現
- 服務提供者provider:暴露服務
- 服務消費者consumer:調用遠程服務
- 監控中心monitor:統計服務的調用次數和調用時間
- 容器container:服務允許容器
調用流程:
1:container容器負責啟動、加載、運行provider
2:provider在啟動時,向regisitry中心注冊自己提供的服務
3:consumer在啟動時,向regisitry中心訂閱自己所需的服務
4:regisitry返回服務提供者列表給consumer,如果有變更,registry將基于長連接推送變更數據給consumer
5:consumer調用provider服務,基于負載均衡算法進行調用
6:consumer調用provider的統計,基于短鏈接定時每分鐘一次統計到monitor
分層:
- 接口服務層( Service):面向開發者,業務代碼、接口、實現等
- 配置層( Config):對外配置接口,以 ServiceConfig 和 ReferenceConfig 為中心
- 服務代理層( Proxy):對生產者和消費者、dubbo都會產生一個代理類封裝調用細節,業務層對遠程調用無感
- 服務注冊層( Registry) : 封裝服務地址的注冊和發現, 以服務 URL 為中心
- 路由層( Cluster) : 封裝多個提供者的路由和負載均衡, 并橋接注冊中心
- 監控層( Monitor) : RPC 調用次數和調用時間監控
- 遠程調用層( Protocal):封裝 RPC 調用
- 信息交換層( Exchange): 封裝請求響應模式, 同步轉異步
- 網絡傳輸層( Transport):抽象 mina 和 netty 為統一接口,統一網絡傳輸接口
- 數據序列化層( Serialize) : 數據傳輸的序列化和反序列化