掌握高併發、高可用架構
第三章 分佈式
本章介紹分佈式架構的底層技術。主要說明面試過程中可能被問到的技術點。
第一節 Dubbo
Duboo
服務治理
Zookeeper
1. Dubbo的概念
Dubbo是一個分佈式、高性能、透明化的RPC(遠程服務調用)服務框架, 提供服務自動註冊、自動發現等高效服務治理方案,可以和Spring無縫集成。
2. Dubbo的由來
當網站流量很小時,可以把所有功能都部署在一個項目中,叫單體應用框架。
隨着流量的增大,應用拆分必不可少,此時會出現多個垂直應用,在美格爾應用中處理各自的業務邏輯。
當垂直應用越來越多,應用之間的交互不可避免,將核心業務抽離出來,作爲獨立的服務,逐漸形成穩定的服務中心。此時,用於提高業務複用和整合的 分佈式服務框架 RPC 是關鍵。
當服務越來越多,容量的評估、小服務資源的浪費等問題逐漸顯現,此時需要增加一個調度中心基於訪問壓力實時管理集羣容量,提高集羣利用率。此時,用於提高集羣利用率的 資源調度和治理中心 SOA 是關鍵。
3. Dubbo的主要特點
- 透明化的遠程方法調用,沒有API侵入
- 軟負載均衡及容錯機制,降低成本,拒絕單點
- 服務自動註冊與發現,不再需要寫死服務提供者地址,註冊中心基於接口名查詢服務提供者的IP地址,並能夠平滑的添加或刪除服務提供者
4. Dubbo的核心組件
Provider
,暴露服務的服務提供方Consumer
,調用遠程服務的服務消費者Registry
,服務註冊與發現的服務中心Monitor
,統計服務的調用次數和調用時間的監控中心Container
,服務運行容器
下面來解釋上述的兩張圖:
- 服務容器啓動、加載、運行服務提供者
- 服務提供者在啓動時,向註冊中心註冊自己提供的服務
- 服務消費者在啓動時,向註冊中心訂閱自己所需的服務,並將獲得的地址列表進行緩存。如果沒有訂閱到自己想訂閱的服務,它會不斷嘗試訂閱
- 註冊中心返回服務提供者地址列表給消費者。當新的服務註冊到註冊中心後,註冊中心會基於長連接將這些服務通過notify到消費者
- 服務消費者從緩存的提供者地址列表中,基於軟負載均衡算法,選一臺進行服務調用(此處有容錯機制)
- 服務提供者和消費者,在各自的內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心
5. Dubbo的核心配置
配置 | 配置說明 |
---|---|
dubbo:service | 服務配置,用於暴露服務,定義服務的元信息,一個服務可用多個協議暴露,也可以註冊到多個註冊中心,對應ServiceBean |
dubbo:reference | 引用配置,用於創建一個遠程服務代理,一個引用可以指向多個註冊中心。對應ReferenceBean |
dubbo:protocol | 協議配置,用於配置提供服務的協議信息,協議由提供方指定,消費方被動接受。對應ProtocolConfig |
dubbo:application | 應用配置,用於配置當前應用信息,不管應用是服務提供方還是消費方 。對應ApplicationConfig |
dubbo:module | 模塊配置,用於配置當前模塊信息,可選。對應ModuleConfig |
dubbo:registry | 註冊中心配置。對應RegistryConfig |
dubbo:monitor | 監控中心配置。對應MonitorConfig |
dubbo:provider | 提供者配置,當沒有配置ProtocolConfig 或ServiceBean 時,採用該配置項,可選。對應ProviderConfig |
dubbo:consumer | 消費方配置,當沒有配置ReferenceBean 時,採用該配置項,可選。對應ConsumerConfig |
dubbo:method | 方法配置 |
dubbo:argument | 參數配置 |
6. Dubbo支持的協議
協議名稱 | 實現描述 | 連接 | 使用場景 |
---|---|---|---|
dubbo協議(默認) | 傳輸:mina、netty、grizzly<br />序列化:hessian2、java、json | 缺省採用單一長連接和NIO異步通信,TCP | 1.傳入傳出參數數據包較小<br />2.消費者數量遠大於生產者<br />3.常規遠程服務調用<br />4.不適合傳輸大數據量的服務,比如文件、視頻等 |
rmi | 傳輸:rmi java<br />序列化:java標準序列化 | 連接個數:多連接<br />連接方式:短連接<br />傳輸協議:TCP/IP<br />傳輸方式:BIO | 1.常規RPC調用<br />2.傳入傳出參數大小包混合<br />3.可傳文件<br />4.不支持防火牆穿透<br />5.生產者和消費者數量差不多 |
hessian | 傳輸:servlet容器<br />序列化:hessian2 | 連接個數:多連接<br />連接方式:短連接<br />傳輸協議:HTTP<br />傳輸方式:同步傳輸 | 1.生產者的數量比消費者多<br />2.可傳文件<br />3.傳入傳輸參數數據包較大<br />4.跨語言傳輸 |
http | 傳輸:servlet容器<br />序列化:表單序列化JSON<br />採用Spring的httpInvoker實現,基於表單的遠程服務調用 | 連接個數:多連接<br />連接方式:短連接<br />傳輸協議:HTTP<br />傳輸方式:同步傳輸 | 1.生產者多餘消費者<br />2.數據包大小混合<br />3.需同時給應用程序和瀏覽器使用的服務 |
webservice | 傳輸:HTTP<br />序列化:SOAP文本序列化 | 連接個數:多連接<br />連接方式:短連接<br />傳輸協議:HTTP<br />傳輸方式:同步傳輸 | 1.系統集成<br />2.跨語言傳輸 |
thrift | 與thrift rpc實現集成,並在基礎上修改了報文頭 | 長連接、NIO異步傳輸 | |
memcached | |||
redis |
7.支持的序列化框架
hessian(默認)、dubbo(不建議)、fastjson、Java自帶序列化
8. 支持的通信框架
默認是Netty的NIO通信,還支持mina、Grizzly、Http等
9. 支持的集羣容錯機制
集羣容錯方案 | 說明 |
---|---|
Failover Cluster(默認) | 失敗自動切換,自動重試其他服務器 |
Failfast Cluster | 快速失敗,立即報錯,只發起一次調用 |
Failsafe Cluster | 失敗安全,出現異常時,直接忽略 |
Failback Cluster | 失敗自動恢復,記錄失敗請求,定時重發 |
Forking Cluster | 並行調用多個服務器,只要一個成功立即返回 |
Broadcast Cluster | 廣播逐個調用所有生產者,任意一個報錯則報錯 |
讀操作建議使用Failover
失敗自動切換,默認重試兩次
寫操作建議使用Failfast
快速失敗,調用一次失敗就報錯
10. 支持的負載均衡算法
負載均衡測量 | 說明 |
---|---|
Random LoadBalance(默認) | 隨機,按權重分配隨機概率 |
RoundRobin LoadBalance | 輪詢,按公約後的權重設置輪詢比率 |
LeastActive LoadBalance | 最少活躍調用數,相同活躍數的隨機 |
ConsistendHash LoadBalance | 一致性Hash,相同參數的請求總是發到同一生產者 |
11. 支持的註冊中心
- Multicast註冊中心
- Zookeeper註冊中心
- Redis註冊中心
- Simple註冊中心
12. 服務暴露和服務消費的過程
服務暴露:在容器啓動時,按照Spring的加載流程初始化BeanDefinition,把服務提供者解析成ServiceBean
。然後調用export()
方法進行暴露。(暴露過程相當長,以後慢慢看吧)
ServiceBean
拿到對外提供服務的實際類ref(如HelloWorldImpl)- 通過
ProxyFactory
的getInvoker()
方法使用ref生成一個AbstractProxyInvoker
實例。到這一步就完成了到Invoker的轉換 - 將
Invoker
轉換爲Exporter
,這個根據不同的協議會有不同的實現- Dubbo的實現:發生在
DubboProtocol
的export()
,主要是打開socket偵聽服務,並接收客戶端發來的各種請求 - RMI的實現:發生在
RmiProtocol
的export()
- Dubbo的實現:發生在
服務消費,分爲消費端的初始化和服務引用過程。
初始化:
- 把服務引用的信息封裝成URL,並註冊到zk中心
- 監聽註冊中心服務的上下線
- 連接服務提供端,創建
NettyClient
對象 - 將這些信息包裝成
DubboInvoker
消費端的調用鏈,創建消費端Invoker實例的服務代理並返回
服務引用:
- 經過負載均衡策略,調用提供者
- 選擇其中一個服務的URL與提供者netty建立連接,使用ProxyFactory創建遠程代理或本地通信的Invoker,並將Invoker發送到netty提供端
- 服務提供端接到該Invoker請求後,找到對應的本地Invoker,處理
- 獲取異步、同步處理結果
- 異步調用,不需要立即拿到返回值,使用
ExchangeClient.send()
- 同步調用,需要立即拿到返回值,使用
ExchangeClient.request()
,返回一個ResponseFuture
,一直阻塞到拿到返回值
- 異步調用,不需要立即拿到返回值,使用
ReferenceBean
的init()
方法調用Protocal
的ref()
方法生成Invoker
實例- 將
Invoker
實例轉換成客戶端需要的接口類