您好,登錄后才能下訂單哦!
這篇文章主要講解了“關(guān)于Dubbo的問題有哪些”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“關(guān)于Dubbo的問題有哪些”吧!
1、RPC
1.1 RPC 定義
互聯(lián)網(wǎng)公司的系統(tǒng)有成千上萬個大大小小的服務(wù)組成,服務(wù)各自部署在不同的機器上,服務(wù)間的調(diào)用需要用到網(wǎng)絡(luò)通信,服務(wù)消費方每調(diào)用一個服務(wù)都要寫一坨網(wǎng)絡(luò)通信相關(guān)的代碼,不僅復(fù)雜而且極易出錯。還要考慮新服務(wù)依賴老服務(wù)時如何調(diào)用老服務(wù),別的服務(wù)依賴新服務(wù)的時候新服務(wù)如何發(fā)布方便他人調(diào)用。如何解決這個問題呢?業(yè)界一般采用RPC遠程調(diào)用的方式來實現(xiàn)。
RPC:
Remote Procedure Call Protocol 既 遠程過程調(diào)用,一種能讓我們像調(diào)用本地服務(wù)一樣調(diào)用遠程服務(wù),可以讓調(diào)用者對網(wǎng)絡(luò)通信這些細節(jié)無感知,比如服務(wù)消費方在執(zhí)行 helloWorldService.sayHello("sowhat") 時,實質(zhì)上調(diào)用的是遠端的服務(wù)。這種方式其實就是RPC,RPC思想在各大互聯(lián)網(wǎng)公司中被廣泛使用,如阿里巴巴的dubbo、當(dāng)當(dāng)?shù)腄ubbox 、Facebook 的 thrift、Google 的grpc、Twitter的finagle等。
1.2 RPC demo
說了那么多,還是實現(xiàn)一個簡易版的RPC demo吧。
1.2.1 公共接口
public interface SoWhatService { String sayHello(String name); }
1.2.2 服務(wù)提供者
接口類實現(xiàn)
public class SoWhatServiceImpl implements SoWhatService { @Override public String sayHello(String name) { return "你好啊 " + name; } }
服務(wù)注冊對外提供者
/** * 服務(wù)注冊對外提供者 */ public class ServiceFramework { public static void export(Object service, int port) throws Exception { ServerSocket server = new ServerSocket(port); while (true) { Socket socket = server.accept(); new Thread(() -> { try { //反序列化 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); //讀取方法名 String methodName =(String) input.readObject(); //參數(shù)類型 Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); //參數(shù) Object[] arguments = (Object[]) input.readObject(); //找到方法 Method method = service.getClass().getMethod(methodName, parameterTypes); //調(diào)用方法 Object result = method.invoke(service, arguments); // 返回結(jié)果 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(result); } catch (Exception e) { e.printStackTrace(); } }).start(); } } }
服務(wù)運行
public class ServerMain { public static void main(String[] args) { //服務(wù)提供者 暴露出接口 SoWhatService service = new SoWhatServiceImpl(); try { ServiceFramework.export(service, 1412); } catch (Exception e) { e.printStackTrace(); } } }
1.2.3 服務(wù)調(diào)用者
動態(tài)代理調(diào)用遠程服務(wù)
/** * @author sowhat * 動態(tài)代理調(diào)用遠程服務(wù) */ public class RpcFunction { public static <T> T refer(Class<T> interfaceClass, String host, int port) throws Exception { return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { //指定 provider 的 ip 和端口 Socket socket = new Socket(host, port); ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); //傳方法名 output.writeObject(method.getName()); //傳參數(shù)類型 output.writeObject(method.getParameterTypes()); //傳參數(shù)值 output.writeObject(arguments); ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); //讀取結(jié)果 Object result = input.readObject(); return result; } }); } }
調(diào)用方法
public class RunMain { public static void main(String[] args) { try { //服務(wù)調(diào)用者 需要設(shè)置依賴 SoWhatService service = RpcFunction.refer(SoWhatService.class, "127.0.0.1", 1412); System.out.println(service.sayHello(" sowhat1412")); } catch (Exception e) { e.printStackTrace(); } } }
2、Dubbo 框架設(shè)計
2.1 Dubbo 簡介
Dubbo 是阿里巴巴研發(fā)開源工具,主要分為2.6.x 跟 2.7.x 版本。是一款分布式、高性能、透明化的 RPC 服務(wù)框架,提供服務(wù)自動注冊、自動發(fā)現(xiàn)等高效服務(wù)治理方案,可以和Spring 框架無縫集成,它提供了6大核心能力:
1. 面向接口代理的高性能RPC調(diào)用
2. 智能容錯和負載均衡
3. 服務(wù)自動注冊和發(fā)現(xiàn)
4. 高度可擴展能力
5. 運行期流量調(diào)度
6. 可視化的服務(wù)治理與運維
調(diào)用過程:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
服務(wù)提供者 Provider 啟動然后向 Registry 注冊自己所能提供的服務(wù)。
服務(wù)消費者 Consumer 向Registry訂閱所需服務(wù),Consumer 解析Registry提供的元信息,從服務(wù)中通過負載均衡選擇 Provider調(diào)用。
服務(wù)提供方 Provider 元數(shù)據(jù)變更的話Registry會把變更推送給Consumer,以此保證Consumer獲得最新可用信息。
注意點:
Provider 跟 Consumer 在內(nèi)存中記錄調(diào)用次數(shù)跟時間,定時發(fā)送統(tǒng)計數(shù)據(jù)到Monitor,發(fā)送的時候是短連接。
Monitor 跟 Registry 是可選的,可直接在配置文件中寫好,Provider 跟 Consumer進行直連。
Monitor 跟 Registry 掛了也沒事, Consumer 本地緩存了 Provider 信息。
Consumer 直接調(diào)用 Provider 不會經(jīng)過 Registry。Provider、Consumer這倆到 Registry之間是長連接。
2.2 Dubbo框架分層
如上圖,總的而言 Dubbo 分為三層。
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
Busines層:由用戶自己來提供接口和實現(xiàn)還有一些配置信息。
RPC層:真正的RPC調(diào)用的核心層,封裝整個RPC的調(diào)用過程、負載均衡、集群容錯、代理。
Remoting層:對網(wǎng)絡(luò)傳輸協(xié)議和數(shù)據(jù)轉(zhuǎn)換的封裝。
如果每一層再細分下去,一共有十層。
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
接口服務(wù)層(Service):該層與業(yè)務(wù)邏輯相關(guān),根據(jù) provider 和 consumer 的業(yè)務(wù)設(shè)計對應(yīng)的接口和實現(xiàn)。
配置層(Config):對外配置接口,以 ServiceConfig 和 ReferenceConfig 為中心初始化配置。
服務(wù)代理層(Proxy):服務(wù)接口透明代理,Provider跟Consumer都生成代理類,使得服務(wù)接口透明,代理層實現(xiàn)服務(wù)調(diào)用跟結(jié)果返回。
服務(wù)注冊層(Registry):封裝服務(wù)地址的注冊和發(fā)現(xiàn),以服務(wù) URL 為中心。
路由層(Cluster):封裝多個提供者的路由和負載均衡,并橋接注冊中心,以Invoker 為中心,擴展接口為 Cluster、Directory、Router 和 LoadBlancce。
監(jiān)控層(Monitor):RPC 調(diào)用次數(shù)和調(diào)用時間監(jiān)控,以 Statistics 為中心,擴展接口為 MonitorFactory、Monitor 和 MonitorService。
遠程調(diào)用層(Protocal):封裝 RPC 調(diào)用,以 Invocation 和 Result 為中心,擴展接口為 Protocal、Invoker 和 Exporter。
信息交換層(Exchange):封裝請求響應(yīng)模式,同步轉(zhuǎn)異步。以 Request 和Response 為中心,擴展接口為 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer。
網(wǎng)絡(luò)傳輸層(Transport):抽象 mina 和 netty 為統(tǒng)一接口,以 Message 為中心,擴展接口為 Channel、Transporter、Client、Server 和 Codec。
數(shù)據(jù)序列化層(Serialize):可復(fù)用的一些工具,擴展接口為 Serialization、ObjectInput、ObjectOutput 和 ThreadPool。
他們之間的調(diào)用關(guān)系直接看下面官網(wǎng)圖即可。
3、Dubbo SPI 機制
Dubbo 采用 微內(nèi)核設(shè)計 + SPI 擴展技術(shù)來搭好核心框架,同時滿足用戶定制化需求。這里重點說下SPI。
3.1 微內(nèi)核
操作系統(tǒng)層面的微內(nèi)核跟宏內(nèi)核:
微內(nèi)核Microkernel:是一種內(nèi)核的設(shè)計架構(gòu),由盡可能精簡的程序所組成,以實現(xiàn)一個操作系統(tǒng)所需要的最基本功能,包括了底層的尋址空間管理、線程管理、與進程間通信。成功案例是QNX系統(tǒng),比如黑莓手機跟車用市場。
宏內(nèi)核Monolithic :把 進程管理、內(nèi)存管理、文件系統(tǒng)、進程通信等功能全部作為內(nèi)核來實現(xiàn),而微內(nèi)核則僅保留最基礎(chǔ)的功能,Linux 就是宏內(nèi)核架構(gòu)設(shè)計。
Dubbo中的廣義微內(nèi)核:
思想是 核心系統(tǒng) + 插件,說白了就是把不變的功能抽象出來稱為核心,把變動的功能作為插件來擴展,符合開閉原則,更容易擴展、維護。比如小霸王游戲機中機體本身作為核心系統(tǒng),游戲片就是插件。vscode、Idea、chrome等都是微內(nèi)核的產(chǎn)物。
微內(nèi)核架構(gòu)其實是一直架構(gòu)思想,可以是框架層面也可以是某個模塊設(shè)計,它的本質(zhì)就是將變化的部分抽象成插件,使得可以快速簡便地滿足各種需求又不影響整體的穩(wěn)定性。
3.2 SPI 含義
主流的數(shù)據(jù)庫有MySQL、Oracle、DB2等,這些數(shù)據(jù)庫是不同公司開發(fā)的,它們的底層協(xié)議不大一樣,那怎么約束呢?一般就是定制統(tǒng)一接口,具體實現(xiàn)不管,反正面向相同的接口編程即可。等到真正使用的時候用具體的實現(xiàn)類就好,問題是哪里找用那個實現(xiàn)類呢?這時候就采用約定好的法則將實現(xiàn)類寫到指定位置即可。
SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機制。它約定在ClassPath路徑下的META-INF/services文件夾查找文件,自動加載文件里所定義的類。
3.3 SPI demo
接口:
package com.example.demo.spi; public interface SPIService { void execute(); }
實現(xiàn)類1:
public class SpiImpl1 implements SPIService{ @Override public void execute() { System.out.println("SpiImpl1.execute()"); } }
實現(xiàn)類2:
public class SpiImpl2 implements SPIService{ @Override public void execute() { System.out.println("SpiImpl2.execute()"); } }
配置路徑
調(diào)用加載類
package com.example.demo.spi; import sun.misc.Service; import java.util.Iterator; import java.util.ServiceLoader; public class Test { public static void main(String[] args) { Iterator<SPIService> providers = Service.providers(SPIService.class); ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class); while(providers.hasNext()) { SPIService ser = providers.next(); ser.execute(); } System.out.println("--------------------------------"); Iterator<SPIService> iterator = load.iterator(); while(iterator.hasNext()) { SPIService ser = iterator.next(); ser.execute(); } } }
3.4 SPI源碼追蹤
ServiceLoader.load(SPIService.class) 底層調(diào)用大致邏輯如下:圖片iterator.hasNext() 跟 iterator.next()底層調(diào)用大致如下:
3.5 Java SPI缺點
不能按需加載,Java SPI在加載擴展點的時候,會一次性加載所有可用的擴展點,很多是不需要的,會浪費系統(tǒng)資源。
獲取某個實現(xiàn)類的方式不夠靈活,只能通過 Iterator 形式獲取,不能根據(jù)某個參數(shù)來獲取對應(yīng)的實現(xiàn)類。
不支持AOP與依賴注入,JAVA SPI可能會丟失加載擴展點異常信息,導(dǎo)致追蹤問題很困難。
3.6 Dubbo SPI
JDK自帶的不好用Dubbo 就自己實現(xiàn)了一個 SPI,該SPI 可以通過名字實例化指定的實現(xiàn)類,并且實現(xiàn)了 IOC 、AOP 與 自適應(yīng)擴展 SPI 。
key = com.sowhat.value
Dubbo 對配置文件目錄的約定,不同于 Java SPI ,Dubbo 分為了三類目錄。
META-INF/services/ :該目錄下 SPI 配置文件是為了用來兼容 Java SPI 。
META-INF/dubbo/ :該目錄存放用戶自定義的 SPI 配置文件。
META-INF/dubbo/internal/ :該目錄存 Dubbo 內(nèi)部使用的 SPI 配置文件。
使用的話很簡單 引入依賴,然后百度教程即可。
@Test void sowhat() { ExtensionLoader<SPIService> spiService = ExtensionLoader.getExtensionLoader(SPIService.class); //按需獲取實現(xiàn)類對象 SPIService demo1 = spiService.getExtension("SpiImpl1"); demo1.execute(); }
3.7 Dubbo SPI源碼追蹤
ExtensionLoader.getExtension 方法的整個思路是 查找緩存是否存在,不存在則讀取SPI文件,通過反射創(chuàng)建類,然后設(shè)置依賴注入這些東西,有包裝類就包裝下,執(zhí)行流程如下圖所示:
說下重要的四個部分:
1.injectExtension IOC
查找 set 方法,根據(jù)參數(shù)找到依賴對象則注入。
2.WrapperClass AOP
包裝類,Dubbo 幫你自動包裝,只需要某個擴展類的構(gòu)造函數(shù)只有一個參數(shù),并且是擴展接口類型,就會被判定為包裝類。
3.Activate
Active 有三個屬性,group 表示修飾在哪個端,是 provider 還是 consumer,value 表示在 URL參數(shù)中出現(xiàn)才會被激活,order 表示實現(xiàn)類的順序。
3.8 Adaptive 自適應(yīng)擴展
需求:根據(jù)配置來進行 SPI 擴展的加載后不想在啟動的時候讓擴展被加載,想根據(jù)請求時候的參數(shù)來動態(tài)選擇對應(yīng)的擴展。實現(xiàn):Dubbo用代理機制實現(xiàn)了自適應(yīng)擴展,為用戶想擴展的接口 通過JDK 或者 Javassist 編譯生成一個代理類,然后通過反射創(chuàng)建實例。實例會根據(jù)本來方法的請求參數(shù)得知需要的擴展類,然后通過 ExtensionLoader.getExtensionLoader(type.class).getExtension(name)來獲取真正的實例來調(diào)用,看個官網(wǎng)樣例。
public interface WheelMaker { Wheel makeWheel(URL url); } // WheelMaker 接口的自適應(yīng)實現(xiàn)類 public class AdaptiveWheelMaker implements WheelMaker { public Wheel makeWheel(URL url) { if (url == null) { throw new IllegalArgumentException("url == null"); } // 1. 調(diào)用 url 的 getXXX 方法獲取參數(shù)值 String wheelMakerName = url.getParameter("Wheel.maker"); if (wheelMakerName == null) { throw new IllegalArgumentException("wheelMakerName == null"); } // 2. 調(diào)用 ExtensionLoader 的 getExtensionLoader 獲取加載器 // 3. 調(diào)用 ExtensionLoader 的 getExtension 根據(jù)從url獲取的參數(shù)作為類名稱加載實現(xiàn)類 WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName); // 4. 調(diào)用實現(xiàn)類的具體方法實現(xiàn)調(diào)用。 return wheelMaker.makeWheel(URL url); } }
查看Adaptive注解源碼可知該注解可用在類或方法上,Adaptive 注解在類上或者方法上有不同的實現(xiàn)邏輯。
7.8.1 Adaptive 注解在類上
Adaptive 注解在類上時,Dubbo 不會為該類生成代理類,Adaptive 注解在類上的情況很少,在 Dubbo 中,僅有兩個類被 Adaptive 注解了,分別是 AdaptiveCompiler 和 AdaptiveExtensionFactory,表示拓展的加載邏輯由人工編碼完成,這不是我們關(guān)注的重點。
7.8.2 Adaptive 注解在方法上
Adaptive 注解在方法上時,Dubbo 則會為該方法生成代理邏輯,表示拓展的加載邏輯需由框架自動生成,大致的實現(xiàn)機制如下:
加載標(biāo)注有 @Adaptive 注解的接口,如果不存在,則不支持 Adaptive 機制;
為目標(biāo)接口按照一定的模板生成子類代碼,并且編譯生成的代碼,然后通過反射生成該類的對象;
結(jié)合生成的對象實例,通過傳入的URL對象,獲取指定key的配置,然后加載該key對應(yīng)的類對象,最終將調(diào)用委托給該類對象進行。
@SPI("apple") public interface FruitGranter { Fruit grant(); @Adaptive String watering(URL url); } --- // 蘋果種植者 public class AppleGranter implements FruitGranter { @Override public Fruit grant() { return new Apple(); } @Override public String watering(URL url) { System.out.println("watering apple"); return "watering finished"; } } --- // 香蕉種植者 public class BananaGranter implements FruitGranter { @Override public Fruit grant() { return new Banana(); } @Override public String watering(URL url) { System.out.println("watering banana"); return "watering success"; } }
調(diào)用方法實現(xiàn):
public class ExtensionLoaderTest { @Test public void testGetExtensionLoader() { // 首先創(chuàng)建一個模擬用的URL對象 URL url = URL.valueOf("dubbo://192.168.0.1:1412?fruit.granter=apple"); // 通過ExtensionLoader獲取一個FruitGranter對象 FruitGranter granter = ExtensionLoader.getExtensionLoader(FruitGranter.class) .getAdaptiveExtension(); // 使用該FruitGranter調(diào)用其"自適應(yīng)標(biāo)注的"方法,獲取調(diào)用結(jié)果 String result = granter.watering(url); System.out.println(result); } }
通過如上方式生成一個內(nèi)部類。大致調(diào)用流程如下:
4、Dubbo 服務(wù)暴露流程
4.1 服務(wù)暴露總覽
Dubbo框架是以URL為總線的模式,運行過程中所有的狀態(tài)數(shù)據(jù)信息都可以通過URL來獲取,比如當(dāng)前系統(tǒng)采用什么序列化,采用什么通信,采用什么負載均衡等信息,都是通過URL的參數(shù)來呈現(xiàn)的,所以在框架運行過程中,運行到某個階段需要相應(yīng)的數(shù)據(jù),都可以通過對應(yīng)的Key從URL的參數(shù)列表中獲取。URL 具體的參數(shù)如下:
protocol:指的是 dubbo 中的各種協(xié)議,如:dubbo thrift http username/password:用戶名/密碼 host/port:主機/端口 path:接口的名稱 parameters:參數(shù)鍵值對
protocol://username:password@host:port/path?k=v
服務(wù)暴露從代碼流程看分為三部分:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
檢查配置,最終組裝成 URL。
暴露服務(wù)到到本地服務(wù)跟遠程服務(wù)。
服務(wù)注冊至注冊中心。
服務(wù)暴露從對象構(gòu)建轉(zhuǎn)換看分為兩步:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
將服務(wù)封裝成Invoker。
將Invoker通過協(xié)議轉(zhuǎn)換為Exporter。
4.2 服務(wù)暴露源碼追蹤
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
容器啟動,Spring IOC 刷新完畢后調(diào)用 onApplicationEvent 開啟服務(wù)暴露,ServiceBean 。
export 跟 doExport 來進行拼接構(gòu)建URL,為屏蔽調(diào)用的細節(jié),統(tǒng)一暴露出一個可執(zhí)行體,通過ProxyFactory 獲取到 invoker。
調(diào)用具體 Protocol 將把包裝后的 invoker 轉(zhuǎn)換成 exporter,此處用到了SPI。
然后啟動服務(wù)器server,監(jiān)聽端口,使用NettyServer創(chuàng)建監(jiān)聽服務(wù)器。
通過 RegistryProtocol 將URL注冊到注冊中心,使得consumer可獲得provider信息。圖片
5、Dubbo 服務(wù)引用流程
Dubbo中一個可執(zhí)行體就是一個invoker,所以 provider 跟 consumer 都要向 invoker 靠攏。通過上面demo可知為了無感調(diào)用遠程接口,底層需要有個代理類包裝 invoker。
服務(wù)的引入時機有兩種:
餓漢式:
通過實現(xiàn) Spring 的 InitializingBean 接口中的 afterPropertiesSet 方法,容器通過調(diào)用 ReferenceBean的 afterPropertiesSet 方法時引入服務(wù)。
懶漢式(默認):
懶漢式是只有當(dāng)服務(wù)被注入到其他類中時啟動引入流程。
服務(wù)引用的三種方式:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
本地引入:服務(wù)暴露時本地暴露,避免網(wǎng)絡(luò)調(diào)用開銷。
直接連接引入遠程服務(wù):不啟動注冊中心,直接寫死遠程Provider地址 進行直連。
通過注冊中心引入遠程服務(wù):通過注冊中心抉擇如何進行負載均衡調(diào)用遠程服務(wù)。
服務(wù)引用流程:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
檢查配置構(gòu)建map ,map 構(gòu)建 URL ,通過URL上的協(xié)議利用自適應(yīng)擴展機制調(diào)用對應(yīng)的 protocol.refer 得到相應(yīng)的 invoker ,此處
想注冊中心注冊自己,然后訂閱注冊中心相關(guān)信息,得到provider的 ip 等信息,再通過共享的netty客戶端進行連接。
當(dāng)有多個 URL 時,先遍歷構(gòu)建出 invoker 然后再由 StaticDirectory 封裝一下,然后通過 cluster 進行合并,只暴露出一個 invoker 。
然后再構(gòu)建代理,封裝 invoker 返回服務(wù)引用,之后 Comsumer 調(diào)用的就是這個代理類。
調(diào)用方式:
oneway:不關(guān)心請求是否發(fā)送成功。
Async異步調(diào)用:Dubbo天然異步,客戶端調(diào)用請求后將返回的 ResponseFuture 存到上下文中,用戶可隨時調(diào)用 future.get 獲取結(jié)果。異步調(diào)用通過唯一ID 標(biāo)識此次請求。
Sync同步調(diào)用:在 Dubbo 源碼中就調(diào)用了 future.get,用戶感覺方法被阻塞了,必須等結(jié)果后才返回。
6、Dubbo 調(diào)用整體流程
調(diào)用之前你可能需要考慮這些事:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
consumer 跟 provider 約定好通訊協(xié)議,dubbo支持多種協(xié)議,比如dubbo、rmi、hessian、http、webservice等。默認走dubbo協(xié)議,連接屬于單一長連接,NIO異步通信。適用傳輸數(shù)據(jù)量很小(單次請求在100kb以內(nèi)),但是并發(fā)量很高。
約定序列化模式,大致分為兩大類,一種是字符型(XML或json 人可看懂 但傳輸效率低),一種是二進制流(數(shù)據(jù)緊湊,機器友好)。默認使用 hessian2作為序列化協(xié)議。
consumer 調(diào)用 provider 時提供對應(yīng)接口、方法名、參數(shù)類型、參數(shù)值、版本號。
provider列表對外提供服務(wù)涉及到負載均衡選擇一個provider提供服務(wù)。
consumer 跟 provider 定時向monitor 發(fā)送信息。
調(diào)用大致流程:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
客戶端發(fā)起請求來調(diào)用接口,接口調(diào)用生成的代理類。代理類生成RpcInvocation 然后調(diào)用invoke方法。
ClusterInvoker獲得注冊中心中服務(wù)列表,通過負載均衡給出一個可用的invoker。
序列化跟反序列化網(wǎng)絡(luò)傳輸數(shù)據(jù)。通過NettyServer調(diào)用網(wǎng)絡(luò)服務(wù)。
服務(wù)端業(yè)務(wù)線程池接受解析數(shù)據(jù),從exportMap找到invoker進行invoke。
調(diào)用真正的Impl得到結(jié)果然后返回。
調(diào)用方式:
oneway:不關(guān)心請求是否發(fā)送成功,消耗最小。
sync同步調(diào)用:在 Dubbo 源碼中就調(diào)用了 future.get,用戶感覺方法被阻塞了,必須等結(jié)果后才返回。
Async 異步調(diào)用:Dubbo天然異步,客戶端調(diào)用請求后將返回的 ResponseFuture 存到上下文中,用戶可以隨時調(diào)用future.get獲取結(jié)果。異步調(diào)用通過唯一ID標(biāo)識此次請求。
7、Dubbo集群容錯負載均衡
Dubbo 引入了Cluster、Directory、Router、LoadBalance、Invoker模塊來保證Dubbo系統(tǒng)的穩(wěn)健性,它們的關(guān)系如下圖:
服務(wù)發(fā)現(xiàn)時會將多個多個遠程調(diào)用放入Directory,然后通過Cluster封裝成一個Invoker,該invoker提供容錯功能。
消費者代用的時候從Directory中通過負載均衡獲得一個可用invoker,最后發(fā)起調(diào)用。
你可以認為Dubbo中的Cluster對上面進行了大的封裝,自帶各種魯棒性功能。
7.1 集群容錯
集群容錯是在消費者端通過Cluster子類實現(xiàn)的,Cluster接口有10個實現(xiàn)類,每個Cluster實現(xiàn)類都會創(chuàng)建一個對應(yīng)的ClusterInvoker對象。核心思想是讓用戶選擇性調(diào)用這個Cluster中間層,屏蔽后面具體實現(xiàn)細節(jié)。
Cluster | Cluster Invoker | 作用 |
---|---|---|
FailoverCluster | FailoverClusterInvoker | 失敗自動切換功能,默認 |
FailfastCluster | FailfastClusterInvoker | 一次調(diào)用,失敗異常 |
FailsafeCluster | FailsafeClusterInvoker | 調(diào)用出錯則日志記錄 |
FailbackCluster | FailbackClusterInvoker | 失敗返空,定時重試2次 |
ForkingCluster | ForkingClusterInvoker | 一個任務(wù)并發(fā)調(diào)用,一個OK則OK |
BroadcastCluster | BroadcastClusterInvoker | 逐個調(diào)用invoker,全可用才可用 |
AvailableCluster | AvailableClusterInvoker | 哪個能用就用那個 |
MergeableCluster | MergeableClusterInvoker | 按組合并返回結(jié)果 |
7.2 智能容錯之負載均衡
Dubbo中一般有4種負載均衡策略。
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
RandomLoadBalance:加權(quán)隨機,它的算法思想簡單。假設(shè)有一組服務(wù)器 servers = [A, B, C],對應(yīng)權(quán)重為 weights = [5, 3, 2],權(quán)重總和為10?,F(xiàn)把這些權(quán)重值平鋪在一維坐標(biāo)值上,[0, 5) 區(qū)間屬于服務(wù)器 A,[5, 8) 區(qū)間屬于服務(wù)器 B,[8, 10) 區(qū)間屬于服務(wù)器 C。接下來通過隨機數(shù)生成器生成一個范圍在 [0, 10) 之間的隨機數(shù),然后計算這個隨機數(shù)會落到哪個區(qū)間上。默認實現(xiàn)。
LeastActiveLoadBalance:最少活躍數(shù)負載均衡,選擇現(xiàn)在活躍調(diào)用數(shù)最少的提供者進行調(diào)用,活躍的調(diào)用數(shù)少說明它現(xiàn)在很輕松,而且活躍數(shù)都是從 0 加起來的,來一個請求活躍數(shù)+1,一個請求處理完成活躍數(shù)-1,所以活躍數(shù)少也能變相的體現(xiàn)處理的快。
RoundRobinLoadBalance:加權(quán)輪詢負載均衡,比如現(xiàn)在有兩臺服務(wù)器 A、B,輪詢的調(diào)用順序就是 A、B、A、B,如果加了權(quán)重,A 比B 的權(quán)重是2:1,那現(xiàn)在的調(diào)用順序就是 A、A、B、A、A、B。
ConsistentHashLoadBalance:一致性 Hash 負載均衡,將服務(wù)器的 IP 等信息生成一個 hash 值,將hash 值投射到圓環(huán)上作為一個節(jié)點,然后當(dāng) key 來查找的時候順時針查找第一個大于等于這個 key 的 hash 值的節(jié)點。一般而言還會引入虛擬節(jié)點,使得數(shù)據(jù)更加的分散,避免數(shù)據(jù)傾斜壓垮某個節(jié)點。如下圖 Dubbo 默認搞了 160 個虛擬節(jié)點。圖片
7.3 智能容錯之服務(wù)目錄
關(guān)于 服務(wù)目錄Directory 你可以理解為是相同服務(wù)Invoker的集合,核心是RegistryDirectory類。具有三個功能。
從注冊中心獲得invoker列表。
監(jiān)控著注冊中心invoker的變化,invoker的上下線。
刷新invokers列表到服務(wù)目錄。
7.4 智能容錯之服務(wù)路由
服務(wù)路由其實就是路由規(guī)則,它規(guī)定了服務(wù)消費者可以調(diào)用哪些服務(wù)提供者。條件路由規(guī)則由兩個條件組成,分別用于對服務(wù)消費者和提供者進行匹配。比如有這樣一條規(guī)則:
host = 10.20.153.14 => host = 10.20.153.12
該條規(guī)則表示 IP 為 10.20.153.14 的服務(wù)消費者只可調(diào)用 IP 為 10.20.153.12 機器上的服務(wù),不可調(diào)用其他機器上的服務(wù)。條件路由規(guī)則的格式如下:
[服務(wù)消費者匹配條件] => [服務(wù)提供者匹配條件]
如果服務(wù)消費者匹配條件為空,表示不對服務(wù)消費者進行限制。如果服務(wù)提供者匹配條件為空,表示對某些服務(wù)消費者禁用服務(wù)。
8、設(shè)計RPC
通讀下Dubbo的大致實現(xiàn)方式后其實就可以依葫蘆畫瓢了,一個RPC框架大致需要下面這些東西:
服務(wù)的注冊跟發(fā)現(xiàn)的搞一個吧,你可以用ZooKeeper或者Redis來實現(xiàn)。
接下來consumer發(fā)起請求的時候你的面向接口編程啊,用到動態(tài)代理來實現(xiàn)調(diào)用。
多個provider提供相同服務(wù)你的用到LoadBalance啊。
最終選擇一個機器后你的約定好通信協(xié)議啊,如何進行序列化跟反序列化呢?
底層就用現(xiàn)成的高性能Netty框架 NIO模式實現(xiàn)唄。
服務(wù)開啟后的有monitor啊。
感謝各位的閱讀,以上就是“關(guān)于Dubbo的問題有哪些”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對關(guān)于Dubbo的問題有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。