分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

java閘瓦發表於2019-05-04

分散式系統(distributed system)是建立在網路之上的軟體系統。正是因為軟體的特性,所以分散式系統具有高度的內聚性和透明性。因此,網路和分散式系統之間的區別更多的在於高層軟體(特別是作業系統),而不是硬體。

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

分散式訊息佇列(MQ)

為什麼使用 MQ?

  • 非同步處理 - 相比於傳統的序列、並行方式,提高了系統吞吐量。
  • 應用解耦 - 系統間通過訊息通訊,不用關心其他系統的處理。
  • 流量削鋒 - 可以通過訊息佇列長度控制請求量;可以緩解短時間內的高併發請求。
  • 日誌處理 - 解決大量日誌傳輸。
  • 訊息通訊 - 訊息佇列一般都內建了高效的通訊機制,因此也可以用在純的訊息通訊。比如實現點對點訊息佇列,或者聊天室等。

如何保證 MQ 的高可用?

  1. 將所有 Broker 和待分配的 Partition 排序
  2. 將第 i 個 Partition 分配到第(i mod n)個 Broker 上
  3. 將第 i 個 Partition 的第 j 個 Replica 分配到第((i + j) mode n)個 Broker 上

MQ 有哪些常見問題?如何解決這些問題?

MQ 的常見問題有:

  1. 訊息的順序問題
  2. 訊息的重複問題

訊息的順序問題

訊息有序指的是可以按照訊息的傳送順序來消費。

假如生產者產生了 2 條訊息:M1、M2,假定 M1 傳送到 S1,M2 傳送到 S2,如果要保證 M1 先於 M2 被消費,怎麼做?

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選
解決方案:

(1)保證生產者 - MQServer - 消費者是一對一對一的關係

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選
缺陷:

  • 並行度就會成為訊息系統的瓶頸(吞吐量不夠)

  • 更多的異常處理,比如:只要消費端出現問題,就會導致整個處理流程阻塞,我們不得不花費更多的精力來解決阻塞的問題。 (2)通過合理的設計或者將問題分解來規避。

  • 不關注亂序的應用實際大量存在

  • 佇列無序並不意味著訊息無序 所以從業務層面來保證訊息的順序而不僅僅是依賴於訊息系統,是一種更合理的方式。

訊息的重複問題

造成訊息重複的根本原因是:網路不可達

所以解決這個問題的辦法就是繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的訊息,應該怎樣處理?

消費端處理訊息的業務邏輯保持冪等性。只要保持冪等性,不管來多少條重複訊息,最後處理的結果都一樣。保證每條訊息都有唯一編號且保證訊息處理成功與去重表的日誌同時出現。利用一張日誌表來記錄已經處理成功的訊息的 ID,如果新到的訊息 ID 已經在日誌表中,那麼就不再處理這條訊息。

Kafka, ActiveMQ, RabbitMQ, RocketMQ 各有什麼優缺點?

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

分散式服務(RPC)

Dubbo 的實現過程?

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

節點角色:

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

呼叫關係:

  1. 務容器負責啟動,載入,執行服務提供者
  2. 服務提供者在啟動時,向註冊中心註冊自己提供的服務。
  3. 服務消費者在啟動時,向註冊中心訂閱自己所需的服務。
  4. 註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推送變更資料給消費者。
  5. 服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。
  6. 服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心。

Dubbo 負載均衡策略有哪些?

Random

  • 隨機,按權重設定隨機概率。
  • 在一個截面上碰撞的概率高,但呼叫量越大分佈越均勻,而且按概率使用權重後也比較均勻,有利於動態調整提供者權重。

RoundRobin

  • 輪循,按公約後的權重設定輪循比率。
  • 存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。

LeastActive

  • 最少活躍呼叫數,相同活躍數的隨機,活躍數指呼叫前後計數差。
  • 使慢的提供者收到更少請求,因為越慢的提供者的呼叫前後計數差會越大。

ConsistentHash

  • 一致性 Hash,相同引數的請求總是發到同一提供者。
  • 當某一臺提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
  • 預設只對第一個引數 Hash,如果要修改,請配置 <dubbo:parameter key="hash.arguments" value="0,1" />
  • 預設用 160 份虛擬節點,如果要修改,請配置 <dubbo:parameter key="hash.nodes" value="320" />

Dubbo 叢集容錯策略 ?

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

  • Failover - 失敗自動切換,當出現失敗,重試其它伺服器。通常用於讀操作,但重試會帶來更長延遲。可通過 retries="2" 來設定重試次數(不含第一次)。
  • Failfast - 快速失敗,只發起一次呼叫,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。
  • Failsafe - 失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作。
  • Failback - 失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於訊息通知操作。
  • Forking - 並行呼叫多個伺服器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。可通過 forks="2" 來設定最大並行數。
  • Broadcast - 播呼叫所有提供者,逐個呼叫,任意一臺報錯則報錯。通常用於通知所有提供者更新快取或日誌等本地資源資訊。

動態代理策略?

Dubbo 作為 RPC 框架,首先要完成的就是跨系統,跨網路的服務呼叫。消費方與提供方遵循統一的介面定義,消費方呼叫介面時,Dubbo 將其轉換成統一格式的資料結構,通過網路傳輸,提供方根據規則找到介面實現,通過反射完成呼叫。也就是說,消費方獲取的是對遠端服務的一個代理(Proxy),而提供方因為要支援不同的介面實現,需要一個包裝層(Wrapper)。呼叫的過程大概是這樣:

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選
消費方的 Proxy 和提供方的 Wrapper 得以讓 Dubbo 構建出複雜、統一的體系。而這種動態代理與包裝也是通過基於 SPI 的外掛方式實現的,它的介面就是ProxyFactory。

@SPI("javassist")
public interface ProxyFactory {
 @Adaptive({Constants.PROXY_KEY})
 <T> T getProxy(Invoker<T> invoker) throws RpcException;
 @Adaptive({Constants.PROXY_KEY})
 <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
複製程式碼

ProxyFactory 有兩種實現方式,一種是基於 JDK 的代理實現,一種是基於 javassist 的實現。ProxyFactory 介面上定義了@SPI("javassist"),預設為 javassist 的實現。

Dubbo 支援哪些序列化協議?Hessian?Hessian 的資料結構?

  1. dubbo 序列化,阿里尚不成熟的 java 序列化實現。
  2. hessian2 序列化:hessian 是一種跨語言的高效二進位制的序列化方式,但這裡實際不是原生的 hessian2 序列化,而是阿里修改過的 hessian lite,它是 dubbo RPC 預設啟用的序列化方式。
  3. json 序列化:目前有兩種實現,一種是採用的阿里的 fastjson 庫,另一種是採用 dubbo 中自已實現的簡單 json 庫,一般情況下,json 這種文字序列化效能不如二進位制序列化。
  4. java 序列化:主要是採用 JDK 自帶的 java 序列化實現,效能很不理想。
  5. Kryo 和 FST:Kryo 和 FST 的效能依然普遍優於 hessian 和 dubbo 序列化。

Hessian 序列化與 Java 預設的序列化區別?

Hessian 是一個輕量級的 remoting on http 工具,採用的是 Binary RPC 協議,所以它很適合於傳送二進位制資料,同時又具有防火牆穿透能力。

  1. Hessian 支援跨語言序列
  2. 比 java 序列化具有更好的效能和易用性
  3. 支援的語言比較多 Protoco Buffer 是什麼?

Protocol Buffer 是 Google 出品的一種輕量 & 高效的結構化資料儲存格式,效能比 Json、XML 真的強!太!多!

Protocol Buffer 的序列化 & 反序列化簡單 & 速度快的原因是:

  1. 編碼 / 解碼 方式簡單(只需要簡單的數學運算 = 位移等等)
  2. 採用 Protocol Buffer 自身的框架程式碼 和 編譯器 共同完成

Protocol Buffer 的資料壓縮效果好(即序列化後的資料量體積小)的原因是:

  1. 採用了獨特的編碼方式,如 Varint、Zigzag 編碼方式等等
  2. 採用 T - L - V 的資料儲存方式:減少了分隔符的使用 & 資料儲存得緊湊

註冊中心掛了可以繼續通訊嗎?

可以。Dubbo 消費者在應用啟動時會從註冊中心拉取已註冊的生產者的地址介面,並快取在本地。每次呼叫時,按照本地儲存的地址進行呼叫。

ZooKeeper 原理是什麼?ZooKeeper 有什麼用?

ZooKeeper 是一個分散式應用協調系統,已經用到了許多分散式專案中,用來完成統一命名服務、狀態同步服務、叢集管理、分散式應用配置項的管理等工作。

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

  1. 每個 Server 在記憶體中儲存了一份資料;
  2. Zookeeper 啟動時,將從例項中選舉一個 leader(Paxos 協議);
  3. Leader 負責處理資料更新等操作(Zab 協議);
  4. 一個更新操作成功,當且僅當大多數 Server 在記憶體中成功修改資料。

Netty 有什麼用?NIO/BIO/AIO 有什麼用?有什麼區別?

Netty 是一個“網路通訊框架”。

Netty 進行事件處理的流程。Channel是連線的通道,是 ChannelEvent 的產生者,而ChannelPipeline可以理解為 ChannelHandler 的集合。

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

IO 的方式通常分為幾種:

  • 同步阻塞的 BIO
  • 同步非阻塞的 NIO
  • 非同步非阻塞的 AIO 在使用同步 I/O 的網路應用中,如果要同時處理多個客戶端請求,或是在客戶端要同時和多個伺服器進行通訊,就必須使用多執行緒來處理。

NIO 基於 Reactor,當 socket 有流可讀或可寫入 socket 時,作業系統會相應的通知引用程式進行處理,應用再將流讀取到緩衝區或寫入作業系統。也就是說,這個時候,已經不是一個連線就要對應一個處理執行緒了,而是有效的請求,對應一個執行緒,當連線沒有資料時,是沒有工作執行緒來處理的。

與 NIO 不同,當進行讀寫操作時,只須直接呼叫 API 的 read 或 write 方法即可。這兩種方法均為非同步的,對於讀操作而言,當有流可讀取時,作業系統會將可讀的流傳入 read 方法的緩衝區,並通知應用程式;對於寫操作而言,當作業系統將 write 方法傳遞的流寫入完畢時,作業系統主動通知應用程式。即可以理解為,read/write 方法都是非同步的,完成後會主動呼叫回撥函式。

為什麼要進行系統拆分?拆分不用 Dubbo 可以嗎?

系統拆分從資源角度分為:應用拆分和資料庫拆分。

從採用的先後順序可分為:水平擴充套件、垂直拆分、業務拆分、水平拆分。

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選
是否使用服務依據實際業務場景來決定。

當垂直應用越來越多,應用之間互動不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提高業務複用及整合的分散式服務框架(RPC)是關鍵。

當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個排程中心基於訪問壓力實時管理叢集容量,提高叢集利用率。此時,用於提高機器利用率的資源排程和治理中心(SOA)是關鍵。

Dubbo 和 Thrift 有什麼區別?

  • Thrift 是跨語言的 RPC 框架。
  • Dubbo 支援服務治理,而 Thrift 不支援。

本文到此結束!!喜歡的朋友別忘了點點關注和贊!!

本文的重點是你有沒有收穫與成長,其餘的都不重要,希望讀者們能謹記這一點。同時我經過多年的收藏目前也算收集到了一套完整的學習資料,包括但不限於:分散式架構、高可擴充套件、高效能、高併發、Jvm效能調優、Spring,MyBatis,Nginx原始碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多個知識點高階進階乾貨,希望對想成為架構師的朋友有一定的參考和幫助 需要更詳細思維導圖和以下資料的可以加入我的java架構交流群:571 617 441

分散式服務(RPC)+分散式訊息佇列(MQ)面試題精選

相關文章