高性能可擴(kuò)展分布式RPC框架Dubbo-內(nèi)核原理揭秘

一债查、前言

整體來(lái)說(shuō),一個(gè)公司業(yè)務(wù)系統(tǒng)的演進(jìn)流程基本都是從單體應(yīng)用到多體應(yīng)用瓜挽。在單體應(yīng)用時(shí),不同業(yè)務(wù)模塊相互調(diào)用直接在本地 JVM 進(jìn)程內(nèi)就可以完成;而變?yōu)槎鄠€(gè)應(yīng)用時(shí)征绸,相互之間進(jìn)行通信的方式就不能簡(jiǎn)單的進(jìn)行本地調(diào)用了久橙,因?yàn)椴煌瑯I(yè)務(wù)模塊部署到了不同的 JVM 進(jìn)程里面,更常見(jiàn)的是部署到了不同的機(jī)器管怠,這時(shí)候一個(gè)高效淆衷、穩(wěn)定的 RPC 遠(yuǎn)程調(diào)用框架就變得非常重要。

Dubbo作為阿里巴巴開(kāi)發(fā)的一個(gè)開(kāi)源的高性能的RPC調(diào)用框架渤弛,其致力于提供高性能和透明化的 RPC 遠(yuǎn)程調(diào)用服務(wù)解決方案祝拯。作為阿里巴巴 SOA 服務(wù)化治理方案的核心框架,目前它已進(jìn)入 Apache 孵化器頂級(jí)項(xiàng)目她肯,前景可謂無(wú)限光明佳头。

二、Dubbo-基礎(chǔ)篇

2.1 Dubbo系統(tǒng)組成概述

使用Dubbo框架搭建的系統(tǒng)架構(gòu)如下:


image.png

如上圖是 Dubbo 的架構(gòu)圖晴氨,其中:

  • 服務(wù)提供方在啟動(dòng)時(shí)候會(huì)注冊(cè)自己提供的服務(wù)到服務(wù)注冊(cè)中心康嘉。

  • 服務(wù)消費(fèi)方在啟動(dòng)時(shí)候會(huì)去服務(wù)注冊(cè)中心訂閱自己需要的服務(wù)的地址列表,然后服務(wù)注冊(cè)中心異步把消費(fèi)方需要的服務(wù)接口的提供者的地址列表返回給服務(wù)消費(fèi)方籽前,服務(wù)消費(fèi)方根據(jù)路由規(guī)則和設(shè)置的負(fù)載均衡算法選擇一個(gè)服務(wù)提供者 IP 進(jìn)行調(diào)用亭珍。

  • 監(jiān)控平臺(tái)主要用來(lái)統(tǒng)計(jì)服務(wù)的調(diào)用次數(shù)和調(diào)用耗時(shí)敷钾,服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計(jì)調(diào)用次數(shù)和調(diào)用耗時(shí)肄梨,并定時(shí)每分鐘發(fā)送一次統(tǒng)計(jì)數(shù)據(jù)到監(jiān)控中心阻荒,監(jiān)控中心則使用數(shù)據(jù)繪制圖表來(lái)顯示,監(jiān)控平臺(tái)不是分布式系統(tǒng)必須的众羡,但是這些數(shù)據(jù)有助于系統(tǒng)運(yùn)維和調(diào)優(yōu)财松。服務(wù)提供者和消費(fèi)者可以直接配置監(jiān)控平臺(tái)的地址,也可以通過(guò)服務(wù)注冊(cè)中心來(lái)獲取纱控。

  • 服務(wù)注冊(cè)中心則負(fù)責(zé)服務(wù)注冊(cè)與發(fā)現(xiàn)辆毡,常見(jiàn)的服務(wù)注冊(cè)中心有zookeeper、etcd甜害。

2.2 Dubbo基礎(chǔ)

本節(jié)主要簡(jiǎn)單的講解Dubbo如何使用舶掖,以及本書(shū)中的demo實(shí)例,建議讀者先閱讀基礎(chǔ)篇在進(jìn)入后面的章節(jié)尔店,因?yàn)楹竺嬲鹿?jié)基本是基于本章的demo進(jìn)行講解的眨攘。

demo中 Consumer 模塊為服務(wù)消費(fèi)者相關(guān),本書(shū)中所有與消費(fèi)端有關(guān)的demo都在該模塊中嚣州,包含普通調(diào)用鲫售、各種異步調(diào)用、泛化調(diào)用该肴、基于擴(kuò)展接口實(shí)現(xiàn)的自定義負(fù)載均衡策略情竹、集群容錯(cuò)策略等等。

其中 Provider 模塊為服務(wù)提供者相關(guān)匀哄,本書(shū)中所有與服務(wù)提供端有關(guān)的demo都在該模塊中秦效,包含服務(wù)接口的實(shí)現(xiàn)類(lèi)、服務(wù)提供方的同步處理請(qǐng)求涎嚼、各種異步處理請(qǐng)求的實(shí)現(xiàn)等等阱州。

其中 SDK 模塊是一個(gè)二方包,用來(lái)存放服務(wù)接口法梯,這是為了代碼復(fù)用苔货,在服務(wù)提供者和消費(fèi)者(泛化調(diào)用除外)的模塊里面都需要引入這個(gè)二方包。

三立哑、Dubbo-高級(jí)篇

3.1 Dubbo分層架構(gòu)

本節(jié)我們從整體上來(lái)看看 Dubbo 的分層架構(gòu)設(shè)計(jì)夜惭,架構(gòu)分層是一個(gè)比較經(jīng)典的模式,比如網(wǎng)絡(luò)中的7層協(xié)議刁憋,每層執(zhí)行固定的功能滥嘴,上層依賴(lài)下層提供的功能,下層對(duì)上層的提供功能至耻,下層的改變對(duì)上層不可見(jiàn)若皱,并且每層都是一個(gè)可被替換的組件镊叁。

如下圖是 Dubbo 官方提供的Dubbo的整體架構(gòu)圖:

image.png

Dubbo 官方提供的該架構(gòu)圖很復(fù)雜,一開(kāi)始我們沒(méi)必要深入細(xì)節(jié)走触,下面我們簡(jiǎn)單講解下其中的主要模塊:

  • 其中 Service 和 Config 層為 API接口層晦譬,是為了方便的讓Dubbo使用方發(fā)布服務(wù)和引用服務(wù);對(duì)于服務(wù)提供方來(lái)說(shuō)需要實(shí)現(xiàn)服務(wù)接口互广,然后使用 ServiceConfig API 來(lái)發(fā)布該服務(wù)敛腌;對(duì)于服務(wù)消費(fèi)方來(lái)說(shuō)需要使用ReferenceConfig 對(duì)服務(wù)接口進(jìn)行代理。Dubbo服務(wù)發(fā)布與引用方可以直接初始化配置類(lèi)惫皱,也可以通過(guò) Spring 配置自動(dòng)生成配置類(lèi)像樊。

  • 其它各層均為 SPI層,SPI 意味著下面各層都是組件化可以被替換的旅敷,這也是 Dubbo 設(shè)計(jì)的比較好的一點(diǎn)生棍。Dubbo 增強(qiáng)了 JDK 中提供的標(biāo)準(zhǔn) SPI 功能,在 Dubbo 中除了 Service 和 Config 層外媳谁,其它各層都是通過(guò)實(shí)現(xiàn)擴(kuò)展點(diǎn)接口來(lái)提供服務(wù)的涂滴;Dubbo 增強(qiáng)的 SPI 增加了對(duì)擴(kuò)展點(diǎn) IoC 和 AOP 的支持,一個(gè)擴(kuò)展點(diǎn)可以直接 setter 注入其它擴(kuò)展點(diǎn)晴音;并且不會(huì)一次性實(shí)例化擴(kuò)展點(diǎn)的所有實(shí)現(xiàn)類(lèi)柔纵,這避免了當(dāng)擴(kuò)展點(diǎn)實(shí)現(xiàn)類(lèi)初始化很耗時(shí),但當(dāng)前還沒(méi)用上它的功能時(shí)仍進(jìn)行加載實(shí)例化锤躁,浪費(fèi)資源的情況搁料;增強(qiáng)的 SPI 是在具體用某一個(gè)實(shí)現(xiàn)類(lèi)的時(shí)候才對(duì)具體實(shí)現(xiàn)類(lèi)進(jìn)行實(shí)例化。后續(xù)會(huì)具體講解 Dubbo 增強(qiáng)的 SPI 的實(shí)現(xiàn)原理进苍。

  • Proxy 服務(wù)代理層:該層主要是對(duì)服務(wù)消費(fèi)端使用的接口進(jìn)行代理加缘,把本地調(diào)用透明的轉(zhuǎn)換為遠(yuǎn)程調(diào)用;另外對(duì)服務(wù)提供方的服務(wù)實(shí)現(xiàn)類(lèi)進(jìn)行代理觉啊,把服務(wù)實(shí)現(xiàn)類(lèi)轉(zhuǎn)換為 Wrapper 類(lèi),這是為了減少反射的調(diào)用沈贝,后面會(huì)具體講解到杠人。Proxy層的SPI擴(kuò)展接口為 ProxyFactory,Dubbo 提供的實(shí)現(xiàn)主要有 JavassistProxyFactory(默認(rèn)使用)和 JdkProxyFactory宋下,用戶(hù)可以實(shí)現(xiàn)ProxyFactory SPI接口嗡善,自定義代理服務(wù)層的實(shí)現(xiàn)。

  • Registry 服務(wù)注冊(cè)中心層:服務(wù)提供者啟動(dòng)時(shí)候會(huì)把服務(wù)注冊(cè)到服務(wù)注冊(cè)中心学歧,消費(fèi)者啟動(dòng)時(shí)候會(huì)去服務(wù)注冊(cè)中心獲取服務(wù)提供者的地址列表罩引,Registry層主要功能是封裝服務(wù)地址的注冊(cè)與發(fā)現(xiàn)邏輯,擴(kuò)展接口 Registry 對(duì)應(yīng)的擴(kuò)展實(shí)現(xiàn)為 ZookeeperRegistry枝笨、RedisRegistry袁铐、MulticastRegistry揭蜒、DubboRegistry等。擴(kuò)展接口 RegistryFactory 對(duì)應(yīng)的擴(kuò)展接口實(shí)現(xiàn)為 DubboRegistryFactory剔桨、DubboRegistryFactory屉更、RedisRegistryFactory、ZookeeperRegistryFactory洒缀。另外該層擴(kuò)展接口Directory實(shí)現(xiàn)類(lèi)有RegistryDirectory瑰谜、StaticDirectory用來(lái)透明的把invoker列表轉(zhuǎn)換為一個(gè)invoker;用戶(hù)可以實(shí)現(xiàn)該層的一系列擴(kuò)展接口,自定義該層的服務(wù)實(shí)現(xiàn)树绩。

  • Cluster 路由層:封裝多個(gè)服務(wù)提供者的路由規(guī)則萨脑、負(fù)載均衡、集群容錯(cuò)的實(shí)現(xiàn)饺饭,并橋接服務(wù)注冊(cè)中心渤早;擴(kuò)展接口 Cluster 對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)有 FailoverCluster(失敗重試)、FailbackCluster(失敗自動(dòng)恢復(fù))砰奕、FailfastCluster(快速失斨虢妗)、FailsafeCluster(失敗安全)军援、ForkingCluster(并行調(diào)用)等仅淑;負(fù)載均衡擴(kuò)展接口 LoadBalance 對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)為 RandomLoadBalance(隨機(jī))、RoundRobinLoadBalance(輪詢(xún))胸哥、LeastActiveLoadBalance(最小活躍數(shù))涯竟、ConsistentHashLoadBalance(一致性hash)等。用戶(hù)可以實(shí)現(xiàn)該層的一系列擴(kuò)展接口空厌,自定義集群容錯(cuò)和負(fù)載均衡策略庐船。

  • Monitor 監(jiān)控層:用來(lái)統(tǒng)計(jì)RPC 調(diào)用次數(shù)和調(diào)用耗時(shí)時(shí)間,擴(kuò)展接口為 MonitorFactory嘲更,對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)為 DubboMonitorFactroy筐钟。用戶(hù)可以實(shí)現(xiàn)該層的MonitorFactory擴(kuò)展接口,實(shí)現(xiàn)自定義監(jiān)控統(tǒng)計(jì)策略赋朦。

  • Protocol 遠(yuǎn)程調(diào)用層:封裝 RPC 調(diào)用邏輯篓冲,擴(kuò)展接口為 Protocol, 對(duì)應(yīng)實(shí)現(xiàn)有 RegistryProtocol宠哄、DubboProtocol壹将、InjvmProtocol 等。

  • Exchange 信息交換層:封裝請(qǐng)求響應(yīng)模式毛嫉,同步轉(zhuǎn)異步诽俯,擴(kuò)展接口 Exchanger,對(duì)應(yīng)擴(kuò)展實(shí)現(xiàn)有 HeaderExchanger 等承粤。

  • Transport 網(wǎng)絡(luò)傳輸層:抽象 mina 和 netty 為統(tǒng)一接口暴区。擴(kuò)展接口為 Channel闯团,對(duì)應(yīng)實(shí)現(xiàn)有 NettyChannel(默認(rèn))、MinaChannel 等;擴(kuò)展接口Transporter對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)有GrizzlyTransporter颜启、MinaTransporter偷俭、NettyTransporter(默認(rèn)實(shí)現(xiàn));擴(kuò)展接口Codec2對(duì)應(yīng)實(shí)現(xiàn)類(lèi)有DubboCodec缰盏、ThriftCodec等

  • Serialize 數(shù)據(jù)序列化層:提供可以復(fù)用的一些工具涌萤,擴(kuò)展接口為 Serialization,對(duì)應(yīng)擴(kuò)展實(shí)現(xiàn)有 DubboSerialization口猜、FastJsonSerialization负溪、Hessian2Serialization、JavaSerialization等济炎,擴(kuò)展接口ThreadPool對(duì)應(yīng)擴(kuò)展實(shí)現(xiàn)有 FixedThreadPool川抡、CachedThreadPool、LimitedThreadPool 等须尚。

綜上可知Dubbo的分層架構(gòu)使得Dubbo的每層的功能都是可被替換的崖堤,這使得Dubbo的擴(kuò)展性極強(qiáng),上面說(shuō)了那么多關(guān)于擴(kuò)展點(diǎn)的東西耐床,那么具體什么是擴(kuò)展點(diǎn)呢密幔,下面看下 Dubbo 擴(kuò)展點(diǎn)一個(gè)簡(jiǎn)單例子。以擴(kuò)展點(diǎn) Protocol 為例:

@SPI("dubbo")
public interface Protocol {
...
}

擴(kuò)展點(diǎn)接口的類(lèi)上面都含有@SPI注解撩轰,這里注解里面的"dubbo"說(shuō)明Protocol擴(kuò)展接口SPI的默認(rèn)實(shí)現(xiàn)是DubboProtocol胯甩。

如果我們想自己寫(xiě)一個(gè) Protocol 擴(kuò)展接口的實(shí)現(xiàn)類(lèi),那么我們需要在實(shí)現(xiàn)類(lèi)所在的 Jar 包內(nèi)的 META-INF/dubbo/ 目錄下創(chuàng)建一個(gè)名字為 org.apache.dubbo.rpc.Protocol 的文本文件堪嫂,然后配置它的內(nèi)容為:

myprotocol=com.alibaba.user.MyProtocol

假設(shè)該實(shí)現(xiàn)類(lèi) MyProtocol 的內(nèi)容如下:

package com.alibaba.user;
public class MyProtocol implemenets Protocol {
// ...
}

那么如何使用我們自定義的擴(kuò)展實(shí)現(xiàn)呢偎箫?Dubbo 配置模塊中,擴(kuò)展點(diǎn)均有對(duì)應(yīng)配置屬性或標(biāo)簽皆串,如下代碼通過(guò)配置標(biāo)簽方式指定使用哪個(gè)擴(kuò)展實(shí)現(xiàn):

<dubbo:protocol name="myprotocol" />

注意這里的 name 必須與 jar 包內(nèi) META-INF/dubbo/ 目錄下 org.apache.dubbo.rpc.Protocol 文件中的等號(hào)左側(cè)的key的名字一致淹办。

3.2 Dubbo內(nèi)核原理

在Dubbo中框架的可擴(kuò)展性是靠適配器原理結(jié)合增強(qiáng)SPI機(jī)制實(shí)現(xiàn)的,本書(shū)中首先會(huì)講解Dubbo的適配器原理恶复,什么是適配器模式娇唯?比如dubbo提供的擴(kuò)展接口Protocol,Protocol的定義如下:

@SPI("dubbo")
public interface Protocol {
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    ....
}

Dubbo則會(huì)使用本書(shū)介紹的動(dòng)態(tài)編譯技術(shù)為接口Protocol生成一個(gè)適配器類(lèi)Protocol$Adaptive的對(duì)象實(shí)例寂玲,Dubbo框架中需要使用Protocol的實(shí)例的時(shí)候?qū)嶋H就是使用的Protocol$Adaptive的對(duì)象實(shí)例來(lái)獲取具體SPI實(shí)現(xiàn)類(lèi),其代碼如下:

package org.apache.dubbo.rpc;
...
public class Protocol$Adaptive implements Protocol {   
 ...
public Exporter export(Invoker invoker) throws RpcException {
    String string;
    ...
    //(1)
    URL uRL = invoker.getUrl();
    String string2 = string = uRL.getProtocol() == null ? "dubbo" : uRL.getProtocol();
    if (string == null) {
        throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (").append(uRL.toString()).append(") use keys([protocol])").toString());
    }
    //(2)
    Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(string);
    //(3)
    return protocol.export(invoker);
}
    

在dubbo框架中protocol的一個(gè)定義為: private static final Protocol protocol =ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();當(dāng)調(diào)用protocol.export(wrapperInvoker)時(shí)候梗摇,實(shí)際是調(diào)用的Protocol$Adaptive的對(duì)象實(shí)例的export方法拓哟,然后后者根據(jù)wrapperInvoker中的url里面的協(xié)議類(lèi)型參數(shù)執(zhí)行代碼(2)使用Dubbo增強(qiáng)SPI方法getExtension獲取對(duì)應(yīng)的SPI實(shí)現(xiàn)類(lèi),然后調(diào)用代碼(3)執(zhí)行具體SPI實(shí)現(xiàn)類(lèi)的export方法伶授。

然后本書(shū)會(huì)講解Dubbo增強(qiáng)的SPI機(jī)制断序,本書(shū)中首先會(huì)借助 java.sql.Driver擴(kuò)展接口講解標(biāo)準(zhǔn)JDK中的SPI實(shí)現(xiàn)原理以及缺陷流纹,然后講解dubbo的增強(qiáng)SPI如何對(duì)其進(jìn)行改進(jìn),如何實(shí)現(xiàn)的擴(kuò)展接口之間自動(dòng)IOC和擴(kuò)展接口的功能增強(qiáng)AOP功能违诗。

然后會(huì)講解Dubbo使用JavaAssist減少反射調(diào)用開(kāi)銷(xiāo):Dubbo會(huì)給每個(gè)服務(wù)提供者的實(shí)現(xiàn)類(lèi)生產(chǎn)一個(gè)Wrapper類(lèi)漱凝,這個(gè)wrapper類(lèi)里面最終調(diào)用服務(wù)提供者的接口實(shí)現(xiàn)類(lèi),wrapper類(lèi)的存在是為了減少反射的調(diào)用诸迟。當(dāng)服務(wù)提供方接受到消費(fèi)方發(fā)來(lái)的請(qǐng)求后需要根據(jù)消費(fèi)者傳遞過(guò)來(lái)的方法名和參數(shù)反射調(diào)用服務(wù)提供者的實(shí)現(xiàn)類(lèi)茸炒,而反射本身是有性能開(kāi)銷(xiāo)的,所以dubbo把每個(gè)服務(wù)提供者的實(shí)現(xiàn)類(lèi)通過(guò)JavaAssist包裝為一個(gè)Wrapper類(lèi)阵苇,那么Wrapper類(lèi)為何能減少反射調(diào)用那壁公?觀(guān)看本書(shū)就可以找到答案

3.3 Dubbo功能實(shí)現(xiàn)原理

講解完畢支撐Dubbo框架的內(nèi)核原理后,本書(shū)會(huì)先從整體剖析Dubbo服務(wù)提供端如何發(fā)布服務(wù)的绅项,這包含發(fā)布本地服務(wù)和發(fā)布遠(yuǎn)程服務(wù)的流程紊册, Dubbo服務(wù)導(dǎo)出分 本地導(dǎo)出與遠(yuǎn)程導(dǎo)出,本地導(dǎo)出使用了 injvm 協(xié)議快耿,是一個(gè)偽協(xié)議囊陡,它不開(kāi)啟端口,不發(fā)起遠(yuǎn)程調(diào)用掀亥,只在 JVM 內(nèi)直接關(guān)聯(lián)撞反,但執(zhí)行 Dubbo 的 Filter 鏈;默認(rèn)下 Dubbo 同時(shí)支持本地導(dǎo)出與遠(yuǎn)程導(dǎo)出協(xié)議铺浇,可以通過(guò)ServiceConfig的setScope方式設(shè)置痢畜,其中配置為none表示不導(dǎo)出服務(wù),為remote表示只導(dǎo)出遠(yuǎn)程服務(wù)鳍侣,為local表示只導(dǎo)出本地服務(wù)丁稀。

你會(huì)知道Dubbo如何實(shí)現(xiàn)的服務(wù)延遲發(fā)布,如何把服務(wù)實(shí)現(xiàn)類(lèi)轉(zhuǎn)換為 Wrapper 類(lèi)倚聚,以便減少反射的調(diào)用线衫,什么時(shí)候構(gòu)建的dubbo的Filter鏈,都有哪些Wrapper類(lèi)對(duì)擴(kuò)展接口的實(shí)現(xiàn)類(lèi)進(jìn)行了功能增強(qiáng)惑折?如何啟動(dòng)的NettyServer對(duì)服務(wù)進(jìn)行監(jiān)聽(tīng)授账,同一個(gè)機(jī)器上的多個(gè)服務(wù)提供接口是啟動(dòng)多個(gè)NettyServer還是一個(gè)?如何做到的惨驶?如何注冊(cè)服務(wù)到服務(wù)注冊(cè)中心的白热?服務(wù)注冊(cè)到zookeeper后,其存儲(chǔ)結(jié)構(gòu)是怎么樣的粗卜?

然后本書(shū)會(huì)講解當(dāng)服務(wù)提供方接受到請(qǐng)求后屋确,如何進(jìn)行處理的,這包含F(xiàn)ilter鏈對(duì)請(qǐng)求的處理,以及如何找到對(duì)應(yīng)的被wrapper類(lèi)包裝后的服務(wù)實(shí)現(xiàn)類(lèi)攻臀,并對(duì)請(qǐng)求進(jìn)行處理焕数,如何實(shí)現(xiàn)的Dubbo的服務(wù)提供端異步執(zhí)行。

然后會(huì)講解Dubbo服務(wù)消費(fèi)端的啟動(dòng)流程刨啸,這個(gè)過(guò)程堡赔,你會(huì)知道如何基于Proxy SPI擴(kuò)展實(shí)現(xiàn)對(duì)服務(wù)接口進(jìn)行代理。與服務(wù)提供端一樣设联,消費(fèi)端可以設(shè)置是否需要本地服務(wù)引用善已,你會(huì)知道在消費(fèi)端如果沒(méi)有指定scope類(lèi)型,在啟動(dòng)時(shí)候會(huì)檢查當(dāng)前jvm內(nèi)是否有導(dǎo)出的服務(wù)仑荐,如果有則自動(dòng)開(kāi)啟本地引用(也就是協(xié)議類(lèi)型修改為injvm)雕拼,則具體調(diào)用時(shí)候會(huì)使用本地暴露的服務(wù)來(lái)提供服務(wù),而不發(fā)起遠(yuǎn)程調(diào)用粘招。

當(dāng)具體發(fā)起遠(yuǎn)程調(diào)用時(shí)候啥寇,你會(huì)知道如何動(dòng)態(tài)從服務(wù)注冊(cè)中心動(dòng)態(tài)訂閱服務(wù)信息的,比如訂閱服務(wù)提供者地址列表洒扎,服務(wù)降級(jí)信息辑甜,服務(wù)路由信息,以及Directory目錄與Router路由服務(wù)袍冷,以及什么時(shí)候構(gòu)建的路由規(guī)則鏈磷醋。

如何啟動(dòng)NettyClient具體發(fā)起遠(yuǎn)程調(diào)用的。然后你會(huì)知道同一個(gè)服務(wù)提供者機(jī)器可以提供多個(gè)服務(wù)胡诗,那么消費(fèi)者機(jī)器需要與同一個(gè)服務(wù)提供者機(jī)器提供的多個(gè)共享連接還是與每個(gè)服務(wù)都建立一個(gè)邓线?消費(fèi)端是啟動(dòng)時(shí)候就與服務(wù)提供者機(jī)器建立好連接?

然后會(huì)講解具體如何發(fā)起一次遠(yuǎn)程調(diào)用煌恢,這個(gè)過(guò)程你會(huì)知道當(dāng)發(fā)起一次rpc調(diào)用時(shí)候會(huì)先經(jīng)過(guò)MockInvoker進(jìn)行處理骇陈,其會(huì)看是否設(shè)置了 force:return 降級(jí)策略,如果設(shè)置了則直接返回 mock 值瑰抵,并不發(fā)起遠(yuǎn)程調(diào)用你雌;否者發(fā)起遠(yuǎn)程調(diào)用,如果遠(yuǎn)程調(diào)用結(jié)果 OK二汛,則直接返回遠(yuǎn)程調(diào)用返回的結(jié)果婿崭;如果遠(yuǎn)程調(diào)用失敗了,則看當(dāng)前是否設(shè)置了 fail:return 的降級(jí)策略肴颊,如果設(shè)置了氓栈,則直接返回 mock 值,否者返回調(diào)用遠(yuǎn)程服務(wù)失敗的具體原因婿着。

如果沒(méi)有設(shè)置服務(wù)降級(jí)策略或者mock服務(wù)颤绕,則會(huì)基于SPI機(jī)制選擇具體的集群容錯(cuò)策略(本文會(huì)詳細(xì)講解常見(jiàn)的Failover幸海、Failfast、Failsafe奥务、Forking、Broadcast這幾種集群容錯(cuò)實(shí)現(xiàn)原理袜硫,以及講解如何自己基于SPI實(shí)現(xiàn)自己的容錯(cuò)策略)氯葬,具體集群容錯(cuò)策略?xún)?nèi)有會(huì)根據(jù)SPI機(jī)制選擇設(shè)置的服務(wù)負(fù)載均衡策略(本文會(huì)詳細(xì)介紹常見(jiàn)的Random、RoundRobin婉陷、LeastActive帚称、ConsistentHash),具體負(fù)載均衡策略?xún)?nèi)會(huì)基于SPI選擇設(shè)置的服務(wù)目錄實(shí)現(xiàn)秽澳,其內(nèi)部維護(hù)了所有服務(wù)提供者的服務(wù)提供者列表與路由規(guī)則闯睹,負(fù)載均衡策略則會(huì)從符合路由規(guī)則的地址列表里面選擇一個(gè)invoker返回,然后最終有該invoker執(zhí)行担神。如果執(zhí)行失敗了楼吃,則根據(jù)具體集群容錯(cuò)策略重新選擇一個(gè)invoker進(jìn)行執(zhí)行....

然后本書(shū)會(huì)講解Dubbo線(xiàn)程模型與線(xiàn)程池策略,Dubbo 默認(rèn)的底層網(wǎng)絡(luò)通訊使用的是 Netty 妄讯,服務(wù)提供方 NettyServer 使用兩級(jí)線(xiàn)程池孩锡,其中 EventLoopGroup(boss) 主要用來(lái)接受客戶(hù)端的鏈接請(qǐng)求,并把接受的請(qǐng)求分發(fā)給 EventLoopGroup(worker) 來(lái)處理亥贸,boss 和 worker 線(xiàn)程組我們稱(chēng)之為 IO 線(xiàn)程躬窜。

如果服務(wù)提供方的邏輯能迅速完成,并且不會(huì)發(fā)起新的 IO 請(qǐng)求炕置,那么直接在 IO 線(xiàn)程上處理會(huì)更快荣挨,因?yàn)檫@減少了線(xiàn)程池調(diào)度與上下文切換開(kāi)銷(xiāo)。但如果處理邏輯較慢朴摊,或者需要發(fā)起新的 IO 請(qǐng)求默垄,比如需要查詢(xún)數(shù)據(jù)庫(kù),則 IO 線(xiàn)程必須派發(fā)請(qǐng)求到新的線(xiàn)程池進(jìn)行處理仍劈,否則 IO 線(xiàn)程會(huì)被阻塞厕倍,將導(dǎo)致不能接收其它請(qǐng)求。

Dubbo中在服務(wù)提供端與消費(fèi)端的IO線(xiàn)程對(duì)請(qǐng)求處理時(shí)候默認(rèn)是把請(qǐng)求轉(zhuǎn)交給dubbo框架的內(nèi)部線(xiàn)程池來(lái)進(jìn)行處理的贩疙,以便可以及時(shí)釋放IO線(xiàn)程讹弯。

根據(jù)IO線(xiàn)程把什么類(lèi)型的消息或者請(qǐng)求交給內(nèi)部線(xiàn)程池來(lái)處理,dubbo提供了不同的線(xiàn)程模型这溅,本書(shū)主要講解Dubbo提供的線(xiàn)程模型AllDispatcher组民、DirectDispatcher、MessageOnlyDispatcher悲靴、ExecutionDispatcher臭胜、ConnectionOrderedDispatcher的實(shí)現(xiàn)原理,以及線(xiàn)程池策略FixedThreadPool、LimitedThreadPool耸三、EagerThreadPool乱陡、CachedThreadPool的實(shí)現(xiàn)原理,以及如何基于SPI自定義自己的線(xiàn)程模型與線(xiàn)程池策略仪壮。

基礎(chǔ)篇我們講解到憨颠,基于Dubbo APi搭建Dubbo服務(wù)時(shí)候,服務(wù)消費(fèi)端引入了一個(gè) SDK 二方包积锅,里面存放著服務(wù)提供端提供的所有接口類(lèi),泛化接口調(diào)用方式主要在服務(wù)消費(fèi)端沒(méi)有 API 接口類(lèi)及模型類(lèi)元(比如入?yún)⒑统鰠⒌?POJO 類(lèi))的情況下使用爽彤。其參數(shù)及返回值中沒(méi)有對(duì)應(yīng)的 POJO 類(lèi),所以所有 POJO 均轉(zhuǎn)換為 Map 表示缚陷。使用泛化調(diào)用時(shí)候服務(wù)消費(fèi)模塊不再需要引入 SDK 二方包适篙,本書(shū)會(huì)詳細(xì)介紹Dubbo中nativejava,true, bean三種泛化調(diào)用的實(shí)現(xiàn)箫爷。

基礎(chǔ)篇我們講到Dubbo提供了隱式參數(shù)傳遞的功能嚷节,即服務(wù)調(diào)用方可以通過(guò)RpcContext.getContext().setAttachment()方法設(shè)置附加屬性鍵值對(duì),然后設(shè)置的值對(duì)可以在服務(wù)提供方服務(wù)方法內(nèi)獲鹊骸丹喻;本書(shū)我們會(huì)詳細(xì)介紹如何在在消費(fèi)端設(shè)置參數(shù),并且如何通過(guò)網(wǎng)絡(luò)把參數(shù)傳遞到服務(wù)提供方翁都,然后服務(wù)提供方如何進(jìn)行獲取碍论。

正如Dubbo官網(wǎng)所說(shuō)dubbo從2.7.0版本開(kāi)始支持所有異步編程接口以CompletableFuture為基礎(chǔ),以便解決2.7.0之前版本異步調(diào)用的不便與功能缺失柄慰。

異步調(diào)用實(shí)現(xiàn)是基于 NIO 的非阻塞能力實(shí)現(xiàn)并行調(diào)用鳍悠,服務(wù)消費(fèi)端不需要啟動(dòng)多線(xiàn)程即可完成并行調(diào)用多個(gè)遠(yuǎn)程服務(wù),相對(duì)多線(xiàn)程開(kāi)銷(xiāo)較小坐搔,如下圖是Dubbo異步調(diào)用鏈路概要流程圖圖:


image.png

本書(shū)我們首先講解dubbo服務(wù)消費(fèi)端的異步調(diào)用藏研,首先講解2.7.0版本前的異步調(diào)用實(shí)現(xiàn)原理,我們會(huì)知道future調(diào)用get()方法方式實(shí)現(xiàn)異步缺點(diǎn)是當(dāng)業(yè)務(wù)線(xiàn)程調(diào)用get()方法后業(yè)務(wù)線(xiàn)程會(huì)被阻塞概行,這不是我們想要的蠢挡,所以dubbo2.7.0版本提供了在CompletableFuture對(duì)象上設(shè)置回調(diào)函數(shù)的方式,讓我們實(shí)現(xiàn)真正的異步調(diào)用凳忙。

在Provider端非異步執(zhí)行時(shí)候业踏,其對(duì)調(diào)用方發(fā)來(lái)的請(qǐng)求的處理是在Dubbo內(nèi)部線(xiàn)程模型的線(xiàn)程池中的線(xiàn)程來(lái)執(zhí)行的,在dubbo中服務(wù)提供方提供的所有的服務(wù)接口都是使用這一個(gè)線(xiàn)程池來(lái)執(zhí)行的涧卵,所以當(dāng)一個(gè)服務(wù)執(zhí)行比較耗時(shí)時(shí)候勤家,可能會(huì)占用線(xiàn)程池中很多線(xiàn)程,這可能就會(huì)導(dǎo)致其他服務(wù)的處理收到影響柳恐。

Provider端異步執(zhí)行則將服務(wù)的處理邏輯從Dubbo內(nèi)部線(xiàn)程池切換到業(yè)務(wù)自定義線(xiàn)程伐脖,避免Dubbo線(xiàn)程池中線(xiàn)程被過(guò)度占用热幔,有助于避免不同服務(wù)間的互相影響。

但是需要注意provider端異步執(zhí)行對(duì)節(jié)省資源和提升RPC響應(yīng)性能是沒(méi)有效果的讼庇,這時(shí)是因?yàn)槿绻?wù)處理比較耗時(shí)绎巨,雖然不是使用Dubbo框架內(nèi)部線(xiàn)程處理,但是還是需要業(yè)務(wù)自己的線(xiàn)程來(lái)處理巫俺,另外副作用還有會(huì)新增一次線(xiàn)程上下文切換(從dubbo內(nèi)部線(xiàn)程池線(xiàn)程切換到業(yè)務(wù)線(xiàn)程)认烁,模型如下圖11.2.0

image.png

本書(shū)首先會(huì)講解基于定義CompletableFuture簽名的接口實(shí)現(xiàn)異步執(zhí)行的實(shí)現(xiàn)原理,然后講解使用AsyncContext實(shí)現(xiàn)異步執(zhí)行原理介汹,最后講解Dubbo的異步調(diào)用與執(zhí)行引入的新問(wèn)題以及如何解決的,這包含引入異步調(diào)用時(shí)候等結(jié)果返回后Filter鏈得不到執(zhí)行的問(wèn)題舶沛,以及異步執(zhí)行時(shí)候上下文參數(shù)傳遞問(wèn)題嘹承。

前面章節(jié)我們介紹了服務(wù)消費(fèi)端一次服務(wù)調(diào)用流程與服務(wù)提供端一次服務(wù)處理流程,但是還是有一些東西是我們沒(méi)有提到的如庭,比如服務(wù)消費(fèi)端如何把服務(wù)請(qǐng)求信息序列化為二進(jìn)制叹卷、服務(wù)提供方又是如何把消費(fèi)端發(fā)送的二進(jìn)制數(shù)據(jù)反序列化為可識(shí)別的POJO對(duì)象、比如Dubbo的應(yīng)用層協(xié)議是怎么樣的坪它。本書(shū)我們就來(lái)一一來(lái)看dubbo是如何做這些的骤竹。

本書(shū)會(huì)首先講解Dubbo協(xié)議,在TCP協(xié)議棧中往毡,每層協(xié)議都有自己的協(xié)議報(bào)文格式蒙揣,比如TCP協(xié)議是網(wǎng)絡(luò)七層模型中的傳輸層,有TCP協(xié)議報(bào)文格式开瞭;在TCP上層是應(yīng)用層懒震,應(yīng)用層協(xié)議常見(jiàn)的有http協(xié)議等,Dubbo協(xié)議作為建立在TCP協(xié)議之上的一種應(yīng)用層協(xié)議嗤详,自然也有自己的協(xié)議包格式个扰,Dubbo協(xié)議也是參考TCP協(xié)議棧中的協(xié)議,協(xié)議內(nèi)容由header和body兩部分組成葱色,本書(shū)會(huì)詳細(xì)介紹協(xié)議header中每個(gè)字段含義递宅。然后講解服務(wù)消費(fèi)方編碼原理,包含當(dāng)服務(wù)消費(fèi)端發(fā)送請(qǐng)求時(shí)候苍狰,如何把請(qǐng)求內(nèi)容封裝為Dubbo協(xié)議幀的办龄。然后講解服務(wù)提供方接受請(qǐng)求后如何對(duì)協(xié)議幀進(jìn)行解碼解決半包粘包問(wèn)題的。

四舞痰、Dubbo-實(shí)踐篇

實(shí)踐篇我們來(lái)探討如何使用Arthas和一些demo來(lái)對(duì)研究Dubbo框架實(shí)現(xiàn)提供便捷土榴,并且基于Netty與CompletableFuture模擬了RPC同步與純異步調(diào)用。

首先本書(shū)會(huì)介紹如何安裝Arthas响牛,然后講解如何使用Arthas查看查看擴(kuò)展接口適配器類(lèi)的源碼玷禽,查看服務(wù)提供端Wrapper類(lèi)的源碼赫段,如何查詢(xún)Dubbo啟動(dòng)后都有哪些Filter,然后通過(guò)Demo驗(yàn)證RoundRobin LoadBalance負(fù)載均衡原理矢赁,然后探討如果根據(jù)IP動(dòng)態(tài)路由調(diào)用Dubbo服務(wù)糯笙。

Dubbo的服務(wù)消費(fèi)端基于CompletableFuture實(shí)現(xiàn)了功能比較豐富的純異步調(diào)用,其實(shí)還不單單是CompletableFuture的功勞撩银,歸根到底是Netty的NIO非阻塞功能提供的底層實(shí)現(xiàn)给涕,本文我們就來(lái)基于CompletableFuture與Netty來(lái)模擬下如何異步發(fā)起遠(yuǎn)程調(diào)用,以及如何使用CompletableFuture本身的功能额获,讓多個(gè)請(qǐng)求的異步結(jié)果進(jìn)行運(yùn)算够庙,以便加深對(duì)dubbo異步調(diào)用實(shí)現(xiàn)原理的理解壮虫。

五抛杨、總結(jié)

如何你對(duì)上面內(nèi)容感興趣,想深入研究税迷,但是無(wú)從入手境肾,那么機(jī)會(huì)來(lái)了剔难,專(zhuān)欄

內(nèi)容包含但是不限于上述內(nèi)容,大家可以?huà)呙栌嗛喸搶?zhuān)欄奥喻。

歡迎關(guān)注微信公眾號(hào):


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末偶宫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子环鲤,更是在濱河造成了極大的恐慌纯趋,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件楔绞,死亡現(xiàn)場(chǎng)離奇詭異结闸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)酒朵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)桦锄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蔫耽,你說(shuō)我怎么就攤上這事结耀。” “怎么了匙铡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵图甜,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我鳖眼,道長(zhǎng)黑毅,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任钦讳,我火速辦了婚禮矿瘦,結(jié)果婚禮上枕面,老公的妹妹穿的比我還像新娘。我一直安慰自己缚去,他們只是感情好潮秘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著易结,像睡著了一般枕荞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搞动,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天躏精,我揣著相機(jī)與錄音,去河邊找鬼鹦肿。 笑死玉控,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狮惜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼碌识,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碾篡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起筏餐,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤开泽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后魁瞪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體穆律,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年导俘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了峦耘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旅薄,死狀恐怖辅髓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情少梁,我是刑警寧澤洛口,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站凯沪,受9級(jí)特大地震影響第焰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妨马,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一挺举、第九天 我趴在偏房一處隱蔽的房頂上張望杀赢。 院中可真熱鬧,春花似錦豹悬、人聲如沸葵陵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)脱篙。三九已至,卻和暖如春伤柄,著一層夾襖步出監(jiān)牢的瞬間绊困,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工适刀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秤朗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓笔喉,卻偏偏與公主長(zhǎng)得像取视,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子常挚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容