今天詳細(xì)的分解一下Dubbo的擴(kuò)展機(jī)制,實(shí)現(xiàn)快速入門,豐富個(gè)人簡歷,提高面試level,給自己增加一點(diǎn)談資,秒變面試小達(dá)人,BAT不是夢。
說真的,從零學(xué)習(xí)Dubbo,看這個(gè)系列足夠了,共10篇,歡迎持續(xù)關(guān)注,相約每天早八點(diǎn)。
三分鐘你將學(xué)會(huì):
- Dubbo的自適應(yīng)擴(kuò)展機(jī)制
- Dubbo的SPI擴(kuò)展機(jī)制
- Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制
- Dubbo的過濾器擴(kuò)展機(jī)制
- Dubbo的負(fù)載均衡擴(kuò)展機(jī)制;
- Dubbo的容錯(cuò)機(jī)制擴(kuò)展;
一、Dubbo擴(kuò)展機(jī)制的概述
Dubbo是一個(gè)高性能的分布式服務(wù)框架,廣泛應(yīng)用于各種規(guī)模和種類的企業(yè)級項(xiàng)目中。在實(shí)際應(yīng)用過程中,Dubbo的核心能力是擴(kuò)展機(jī)制,它可以讓Dubbo具有更強(qiáng)的可定制化能力,也可以讓Dubbo更好地適應(yīng)各種應(yīng)用場景。
Dubbo的擴(kuò)展機(jī)制
Dubbo的擴(kuò)展機(jī)制主要包括:自適應(yīng)擴(kuò)展機(jī)制、SPI擴(kuò)展機(jī)制、自定義擴(kuò)展點(diǎn)機(jī)制、過濾器擴(kuò)展機(jī)制、負(fù)載均衡擴(kuò)展機(jī)制和容錯(cuò)機(jī)制擴(kuò)展。
這些機(jī)制使得Dubbo的使用更加靈活方便,可以滿足不同需要的業(yè)務(wù)場景,也可以根據(jù)實(shí)際情況來選擇合適的擴(kuò)展機(jī)制。
在Dubbo的擴(kuò)展機(jī)制中,尤其需要注意自定義擴(kuò)展點(diǎn)機(jī)制和SPI擴(kuò)展機(jī)制。這些機(jī)制是Dubbo中較為重要和常用的擴(kuò)展機(jī)制,充分了解這些機(jī)制可以讓應(yīng)用程序更加靈活和可定制。
下圖展示了Dubbo擴(kuò)展機(jī)制的調(diào)用流程:
Dubbo擴(kuò)展機(jī)制
上圖中,Dubbo客戶端首先會(huì)通過ExtensionLoader加載需要使用的擴(kuò)展點(diǎn),ExtensionLoader會(huì)根據(jù)客戶端傳入的擴(kuò)展點(diǎn)名和配置,創(chuàng)建對應(yīng)的擴(kuò)展點(diǎn)實(shí)例,并返回給客戶端,客戶端再通過返回的擴(kuò)展點(diǎn)實(shí)例調(diào)用相應(yīng)的方法。
二、Dubbo的自適應(yīng)擴(kuò)展機(jī)制
1、什么是自適應(yīng)擴(kuò)展機(jī)制
自適應(yīng)擴(kuò)展機(jī)制是Dubbo提供的一種機(jī)制,它可以使Dubbo框架根據(jù)實(shí)際使用情況動(dòng)態(tài)地選擇不同的擴(kuò)展實(shí)現(xiàn),從而達(dá)到最優(yōu)的效果。
?自適應(yīng)擴(kuò)展機(jī)制的實(shí)現(xiàn)方式是通過在擴(kuò)展接口的代理類中,根據(jù)實(shí)際情況動(dòng)態(tài)地生成對應(yīng)擴(kuò)展實(shí)現(xiàn)的代理類實(shí)例。
下圖是自適應(yīng)擴(kuò)展機(jī)制的詳細(xì)時(shí)序圖:
自適應(yīng)擴(kuò)展機(jī)制
上圖中:
- Client先調(diào)用ExtensionLoader加載擴(kuò)展點(diǎn),并解析配置文件;
- ExtensionLoader根據(jù)配置文件查找實(shí)現(xiàn)類
- 然后創(chuàng)建一個(gè)AdaptiveExtension的代理對象,并將該代理對象返回給Client;
- Client調(diào)用代理對象的方法時(shí),AdaptiveExtension會(huì)根據(jù)配置使用具體的擴(kuò)展點(diǎn)實(shí)現(xiàn),并將調(diào)用轉(zhuǎn)發(fā)給具體的擴(kuò)展點(diǎn)實(shí)現(xiàn);
- 最后將結(jié)果返回給Client;
2、自適應(yīng)擴(kuò)展機(jī)制的使用示例
在Dubbo框架中,有一個(gè)名為Protocol的擴(kuò)展接口,它有多種不同的實(shí)現(xiàn)方式,如dubbo、rmi、http等。在使用Dubbo時(shí),我們可以通過@Reference注解來注入對應(yīng)的擴(kuò)展實(shí)現(xiàn),如:
@Reference(protocol = "dubbo")
private DemoService demoService;
在上述代碼中,我們指定了使用dubbo協(xié)議的DemoService接口的擴(kuò)展實(shí)現(xiàn)。
我們也可以通過adaptive屬性來實(shí)現(xiàn)自適應(yīng)調(diào)用,如:
@Reference(adaptive = "true")
private Protocol protocol;
在上述代碼中,我們使用了adaptive屬性,并注入了Protocol類型的實(shí)例。這時(shí),Dubbo框架會(huì)根據(jù)實(shí)際情況動(dòng)態(tài)地生成對應(yīng)實(shí)現(xiàn)的代理類,并返回對應(yīng)的實(shí)例。
三、Dubbo的SPI擴(kuò)展機(jī)制
1、什么是SPI擴(kuò)展機(jī)制
Dubbo使用了JAVA的SPI(Service Provider Interface)擴(kuò)展機(jī)制。SPI是JDK內(nèi)置的一種服務(wù)發(fā)現(xiàn)機(jī)制,其具體實(shí)現(xiàn)方式是在資源文件META-INF/services中通過名稱為SPI接口的全限定類名創(chuàng)建一個(gè)文本文件,在這個(gè)文本文件中可以寫入該SPI接口的實(shí)現(xiàn)類全限定類名,這樣可以實(shí)現(xiàn)動(dòng)態(tài)加載實(shí)現(xiàn)類的目的。
?Dubbo中的SPI擴(kuò)展機(jī)制能夠在不修改核心源代碼的前提下,通過修改配置文件或者實(shí)現(xiàn)自定義拓展類的方式來替換或增加核心功能。
下圖描述了 Dubbo SPI 擴(kuò)展機(jī)制的工作流程:
SPI 擴(kuò)展機(jī)制
上圖描述了 Dubbo SPI 擴(kuò)展機(jī)制的工作流程,其中:
- 用戶向 Dubbo Framework 請求獲取 ExtensionLoader,ExtensionLoader 是 Dubbo SPI 擴(kuò)展機(jī)制的核心類;
- Dubbo Framework 加載 ExtensionLoader,并返回給用戶;
- 用戶調(diào)用 ExtensionLoader 的方法;
- ExtensionLoader 根據(jù)指定的 Extension 接口,通過 SPI 機(jī)制加載 Extension 實(shí)現(xiàn);
- Extension 實(shí)現(xiàn)將被加載,ExtensionLoader 返回 Extension 實(shí)現(xiàn)給用戶;
2、SPI擴(kuò)展機(jī)制的使用示例
首先,我們需要定義一個(gè)SPI擴(kuò)展接口,讓Dubbo的擴(kuò)展實(shí)現(xiàn)類都實(shí)現(xiàn)該接口。
示例代碼:
package com.example.extension;
import com.alibaba.dubbo.common.extension.SPI;
@SPI("default")
public interface PrintService {
void print(String msg);
}
在接口上添加@SPI注解,指定該擴(kuò)展的默認(rèn)實(shí)現(xiàn)類。
然后,我們需要在META-INF/services目錄下創(chuàng)建一個(gè)“接口全限定類名”的文件名的文件,文件中寫入我們實(shí)現(xiàn)的SPI擴(kuò)展類的全限定類名。
比如我們需要同過實(shí)現(xiàn)PrintService接口來實(shí)現(xiàn)打印功能,那么我們在META-INF/services/目錄下創(chuàng)建一個(gè)名為“com.example.extension.PrintService”的文件,文件內(nèi)容為:
com.example.extension.impl.ConsolePrintServiceImpl
接下來,我們就可以通過Dubbo框架自動(dòng)加載通過SPI機(jī)制注冊的實(shí)現(xiàn)類了。
示例代碼:
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Main.class);
PrintService printService =
ExtensionLoader.getExtensionLoader(PrintService.class).getDefaultExtension();
printService.print("hello world!");
以上代碼中,我們使用Dubbo的擴(kuò)展加載器ExtensionLoader來獲取PrintService接口的默認(rèn)實(shí)現(xiàn)類,然后調(diào)用該實(shí)現(xiàn)類的print()方法即可實(shí)現(xiàn)打印功能。
3、Dubbo的SPI擴(kuò)展機(jī)制中自定義擴(kuò)展點(diǎn)的實(shí)現(xiàn)示例
在Dubbo框架中,我們可以通過自定義擴(kuò)展點(diǎn)來增強(qiáng)Dubbo的功能。
自定義擴(kuò)展點(diǎn)需要實(shí)現(xiàn)Dubbo提供的ExtensionFactory接口,并在META-INF/dubbo/internal/?路徑下創(chuàng)建一個(gè)文件名為com.alibaba.dubbo.common.extension.ExtensionFactory的文件,文件中寫入擴(kuò)展點(diǎn)實(shí)現(xiàn)類的全限定類名。
示例代碼:
package com.example.extension;
import com.alibaba.dubbo.common.extension.ExtensionFactory;
public class MyExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
if (type.equals(PrintService.class)) {
return (T) new ConsolePrintServiceImpl();
}
return null;
}
}
在MyExtensionFactory中實(shí)現(xiàn)getExtension()方法,并根據(jù)type參數(shù)判斷獲取哪個(gè)擴(kuò)展實(shí)現(xiàn)類。
在本示例中,我們僅僅實(shí)現(xiàn)了PrintService接口的實(shí)現(xiàn)類,因此只需要判斷type參數(shù)是否為PrintService類即可。
下一步,我們需要在META-INF/dubbo/internal/目錄下創(chuàng)建一個(gè)名為com.alibaba.dubbo.common.extension.ExtensionFactory的文件,文件內(nèi)容為我們實(shí)現(xiàn)的擴(kuò)展點(diǎn)實(shí)現(xiàn)類全限定類名。
比如我們實(shí)現(xiàn)的擴(kuò)展點(diǎn)實(shí)現(xiàn)類為com.example.extension.MyExtensionFactory?,那么我們就要在META-INF/dubbo/internal/目錄下創(chuàng)建一個(gè)名為com.alibaba.dubbo.common.extension.ExtensionFactory?的文件,并將文件內(nèi)容寫為com.example.extension.MyExtensionFactory。
最后,我們在程序中就可以使用自定義的擴(kuò)展點(diǎn)了。示例代碼:
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Main.class);
PrintService printService =
ExtensionLoader.getExtensionLoader(PrintService.class).getExtension("console");
printService.print("hello world!");
在以上示例代碼中,我們通過getExtension()方法來獲取PrintService接口的實(shí)現(xiàn)類。getExtension()方法中的參數(shù)為擴(kuò)展點(diǎn)的name屬性,該屬性值默認(rèn)為“default”。
在本示例中我們將name的值設(shè)置為“console”,因此即使用了我們自定義的擴(kuò)展點(diǎn)實(shí)現(xiàn)類。
四、Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制
1、什么是自定義擴(kuò)展點(diǎn)機(jī)制
Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制是在SPI擴(kuò)展機(jī)制的基礎(chǔ)上,增加了自定義擴(kuò)展點(diǎn)的實(shí)現(xiàn)方式。通過Dubbo的擴(kuò)展機(jī)制,我們可以通過配置文件切換Dubbo內(nèi)部的實(shí)現(xiàn)方式,但是對于用戶自己實(shí)現(xiàn)的功能模塊,如何進(jìn)行擴(kuò)展呢?
這里就需要用到自定義擴(kuò)展點(diǎn)機(jī)制了。
下圖是自定義擴(kuò)展點(diǎn)機(jī)制的詳細(xì)時(shí)序圖:
自定義擴(kuò)展點(diǎn)機(jī)制
在上圖中:
- 用戶首先將自己實(shí)現(xiàn)的擴(kuò)展點(diǎn)注冊到Dubbo中;
- 然后在需要使用該擴(kuò)展點(diǎn)的時(shí)候,Dubbo會(huì)根據(jù)擴(kuò)展點(diǎn)的名稱進(jìn)行查找并返回相應(yīng)的擴(kuò)展點(diǎn)實(shí)例;
- 通過這樣的機(jī)制,用戶可以靈活地?cái)U(kuò)展Dubbo的功能,同時(shí)也可以讓Dubbo更加適應(yīng)不同的業(yè)務(wù)場景。
自定義擴(kuò)展點(diǎn)的核心思想就是:“面向接口編程,實(shí)現(xiàn)類實(shí)現(xiàn)接口,接口與實(shí)現(xiàn)類通過擴(kuò)展點(diǎn)Binder關(guān)聯(lián)。”
其中,Binder的定義可以參考以下的代碼:
public interface ExtensionFactory {
// 返回一個(gè)擴(kuò)展點(diǎn)的代理對象
<T> T getExtension(Class<T> type, String name) throws IllegalStateException;
}
public interface ExtensionLoader<T> {
T getExtension(String name);
}
public interface ExtensionBinder<T> {
// 綁定
void bind(T instance);
// 獲取綁定的擴(kuò)展對象
T get();
}
public interface ExtensionLoaderListener {
void onLoad();
}
2、自定義擴(kuò)展點(diǎn)機(jī)制的使用示例
為了更好地理解Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制,我們可以通過一個(gè)簡單的示例來演示其使用方法。假設(shè)我們有一個(gè)接口HelloService,我們想要通過自定義擴(kuò)展點(diǎn)機(jī)制,為這個(gè)接口添加一個(gè)實(shí)現(xiàn)類。
首先,我們需要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)類HelloServiceImpl,該實(shí)現(xiàn)類需要實(shí)現(xiàn)HelloService接口。
接著,我們需要在resources/META-INF/dubbo目錄下創(chuàng)建一個(gè)名為com.xxx.HelloService的文件,該文件中需要指定HelloService接口的實(shí)現(xiàn)類名稱。
helloService=com.xxx.HelloServiceImpl
接下來,我們需要在代碼中獲取HelloService接口的實(shí)例。這可以通過以下方式實(shí)現(xiàn):
ExtensionLoader<HelloService> loader =
ExtensionLoader.getExtensionLoader(HelloService.class);
HelloService helloService =
loader.getExtension("helloService");
helloService.sayHello();
其中,getExtensionLoader()方法用于獲取擴(kuò)展點(diǎn)的ExtensionLoader實(shí)例,getExtension()方法用于 獲取具體的擴(kuò)展實(shí)例。
在上面的代碼中,我們通過“helloService”這個(gè)名稱獲取到了實(shí)現(xiàn)了HelloService接口的HelloServiceImpl實(shí)例,并調(diào)用了其中的sayHello()方法。
通過上述示例,我們可以看出,使用Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制非常簡單,只需要在配置文件中指定實(shí)現(xiàn)類的名稱,然后通過getExtensionLoader()和getExtension()方法獲取實(shí)例即可。
3、Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制的實(shí)現(xiàn)示例
在Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制中,最核心的是ExtensionLoader類和ExtensionFactory類。
其中,ExtensionLoader用于加載和管理擴(kuò)展實(shí)例,ExtensionFactory用于創(chuàng)建擴(kuò)展實(shí)例。
下面,我們將通過一個(gè)簡單的示例,演示Dubbo的自定義擴(kuò)展點(diǎn)機(jī)制的具體實(shí)現(xiàn)方式。
首先,我們需要定義一個(gè)擴(kuò)展點(diǎn)接口:
public interface HelloService {
String sayHello(String name);
}
接著,我們需要實(shí)現(xiàn)該接口的一個(gè)實(shí)現(xiàn)類:
@SPI("helloWorld")
public class HelloWorldService implements HelloService {
@Override
public String sayHello(String name) {
return "Hello, " + name;
}
}
在這里,我們使用了@SPI注解來指定該擴(kuò)展點(diǎn)的默認(rèn)實(shí)現(xiàn),如果配置文件中沒有指定其他實(shí)現(xiàn),則會(huì)使用該默認(rèn)實(shí)現(xiàn)。
接下來,我們需要?jiǎng)?chuàng)建一個(gè)名為com.xxx.HelloService的文件,該文件中需要指定擴(kuò)展點(diǎn)接口的實(shí)現(xiàn)類名稱:
helloWorld=com.xxx.HelloWorldService
最后,我們需要在代碼中獲取HelloService接口的實(shí)例,這可以通過以下代碼實(shí)現(xiàn):
ExtensionLoader<HelloService> loader =
ExtensionLoader.getExtensionLoader(HelloService.class);
HelloService helloService =
loader.getExtension("helloWorld");
System.out.println(helloService.sayHello("Dubbo"));
在上述代碼中,我們通過getExtensionLoader()方法獲取HelloService接口的ExtensionLoader實(shí)例,然后通過getExtension()方法獲取名為“helloWorld”的實(shí)現(xiàn)類實(shí)例,并調(diào)用其中的sayHello()方法。
五、Dubbo的過濾器擴(kuò)展機(jī)制
1、Dubbo的過濾器機(jī)制概述
Dubbo的過濾器機(jī)制允許在調(diào)用前、調(diào)用后以及拋出異常時(shí)執(zhí)行一些額外的操作。過濾器在調(diào)用鏈路中按順序執(zhí)行,可以在過濾器中實(shí)現(xiàn)各種功能,例如:日志記錄、性能統(tǒng)計(jì)、權(quán)限控制等。
內(nèi)置過濾器
Dubbo中內(nèi)置了多個(gè)過濾器,包括:ClassLoader過濾器、Context過濾器、Generic過濾器、Echo過濾器、Token過濾器、AccessLog過濾器等。
下面是Dubbo的過濾器機(jī)制的時(shí)序圖:
上圖中:
- 服務(wù)消費(fèi)者向服務(wù)提供者發(fā)送請求時(shí),請求先經(jīng)過過濾器1;
- 如果過濾器1通過則進(jìn)一步經(jīng)過過濾器2;
- 如果過濾器2通過則進(jìn)一步經(jīng)過過濾器3;
- 如果過濾器3通過則將請求發(fā)送給服務(wù)提供者,服務(wù)提供者處理請求后將響應(yīng)返回給服務(wù)消費(fèi)者,響應(yīng)也會(huì)經(jīng)過相同的過濾器鏈路;
- 如果任意一個(gè)過濾器拒絕請求,則直接返回錯(cuò)誤響應(yīng)。
2、過濾器擴(kuò)展機(jī)制的使用示例
Dubbo提供了擴(kuò)展機(jī)制,可以在dubbo配置文件中配置過濾器,示例如下:
<dubbo:provider filter="accessLogFilter" />
在上面的例子中,accessLogFilter表示需要使用的過濾器名稱,可以在dubbo配置文件中通過dubbo:filter標(biāo)簽進(jìn)行定義。
3、自定義過濾器的實(shí)現(xiàn)示例
要實(shí)現(xiàn)自定義過濾器,需要按照以下步驟進(jìn)行:
- 定義一個(gè)類實(shí)現(xiàn)org.Apache.dubbo.rpc.Filter接口;
- 實(shí)現(xiàn)接口中的方法;
- 在META-INF/dubbo目錄下創(chuàng)建一個(gè)以org.apache.dubbo.rpc.Filter接口全限定名為名稱的文件,并在文件中添加自定義過濾器的類名。
下面是一個(gè)自定義的過濾器示例:
package com.example;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
@Activate(group = "provider")
public class MyFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 在這里實(shí)現(xiàn)自己的邏輯
return invoker.invoke(invocation);
}
}
在上面的例子中,我們實(shí)現(xiàn)了一個(gè)MyFilter過濾器,并使用@Activate注解指定了它是一個(gè)provider端的過濾器,然后在invoke()方法中編寫自己的邏輯,最后調(diào)用invoker.invoke(invocation)方法來執(zhí)行調(diào)用鏈路中的下一個(gè)過濾器或服務(wù)。
六、Dubbo的負(fù)載均衡擴(kuò)展機(jī)制
1、Dubbo的負(fù)載均衡擴(kuò)展機(jī)制概述
負(fù)載均衡是分布式系統(tǒng)中的一個(gè)重要問題,它可以實(shí)現(xiàn)將請求分?jǐn)偟蕉鄠€(gè)服務(wù)提供者上,提高系統(tǒng)的并發(fā)能力和可用性。
Dubbo的負(fù)載均衡擴(kuò)展機(jī)制允許用戶自定義負(fù)載均衡策略,實(shí)現(xiàn)更加靈活、適合特定場景的負(fù)載均衡算法。
Dubbo內(nèi)置了多種負(fù)載均衡算法,包括隨機(jī)、輪詢、最少活躍調(diào)用等。
下面是Dubbo的負(fù)載均衡擴(kuò)展機(jī)制的時(shí)序圖:
負(fù)載均衡擴(kuò)展機(jī)制
2、負(fù)載均衡擴(kuò)展機(jī)制的使用示例
Dubbo的負(fù)載均衡擴(kuò)展機(jī)制可以通過在服務(wù)提供方和服務(wù)消費(fèi)方的配置文件中指定負(fù)載均衡策略來使用。
例如,在服務(wù)提供方的配置文件中可以添加以下配置:
<dubbo:service interface="com.xxx.XxxService" loadbalance="roundrobin" />
在服務(wù)消費(fèi)方的配置文件中可以添加以下配置:
<dubbo:reference interface="com.xxx.XxxService" loadbalance="random" />
這樣就可以實(shí)現(xiàn)使用Dubbo內(nèi)置的輪詢
3、自定義負(fù)載均衡策略的實(shí)現(xiàn)示例
用戶可以通過實(shí)現(xiàn)Dubbo的LoadBalance接口來自定義負(fù)載均衡策略。
以下是一個(gè)示例:
public class MyLoadBalance implements LoadBalance {
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
// 自定義負(fù)載均衡算法實(shí)現(xiàn)
return invokers.get(0);
}
}
七、Dubbo的容錯(cuò)機(jī)制擴(kuò)展
1、Dubbo的容錯(cuò)機(jī)制概述
Dubbo的容錯(cuò)機(jī)制是指當(dāng)Dubbo服務(wù)調(diào)用出現(xiàn)異常時(shí),Dubbo框架會(huì)根據(jù)預(yù)設(shè)的容錯(cuò)機(jī)制進(jìn)行處理,以保證服務(wù)的高可用性。
Dubbo框架默認(rèn)提供了多種容錯(cuò)機(jī)制,如Failover、Failfast、Failsafe、Failback、Forking等,也支持自定義容錯(cuò)機(jī)制。
Dubbo的容錯(cuò)機(jī)制通常是通過在客戶端代理層實(shí)現(xiàn)的,當(dāng)遠(yuǎn)程服務(wù)調(diào)用出現(xiàn)異常時(shí),客戶端代理會(huì)根據(jù)預(yù)設(shè)的容錯(cuò)機(jī)制進(jìn)行重試或處理,以保證服務(wù)的高可用性。
容錯(cuò)機(jī)制
在Dubbo的容錯(cuò)機(jī)制中,ClusterInvoker負(fù)責(zé)調(diào)用遠(yuǎn)程服務(wù),并進(jìn)行容錯(cuò)處理。當(dāng)調(diào)用遠(yuǎn)程服務(wù)發(fā)生異常時(shí),Dubbo會(huì)按照以下順序進(jìn)行容錯(cuò)處理:
- ClusterInvoker處理異常;
- 如果ClusterInvoker處理異常失敗,則交由Router處理異常;
- 如果Router處理異常失敗,則交由LoadBalance處理異常;
- 如果LoadBalance處理異常失敗,則拋出異常給InvokerInvocationHandler,最終拋出給Consumer。同時(shí),Dubbo還會(huì)將異常信息進(jìn)行監(jiān)控,并更新調(diào)用統(tǒng)計(jì)信息。
2、容錯(cuò)機(jī)制擴(kuò)展的使用示例
Dubbo默認(rèn)的容錯(cuò)機(jī)制是Failover,即自動(dòng)切換重試其他節(jié)點(diǎn),達(dá)到容錯(cuò)和負(fù)載均衡的效果。如果需要使用其他容錯(cuò)機(jī)制,可以通過在服務(wù)提供方和服務(wù)消費(fèi)方的配置文件中進(jìn)行配置。
例如,我們可以通過以下方式配置使用Failfast容錯(cuò)機(jī)制:
在服務(wù)提供方的配置文件中增加如下配置:
<dubbo:service interface="com.example.service.SomeService" retries="0"/>
在服務(wù)消費(fèi)方的配置文件中增加如下配置:
<dubbo:reference interface="com.example.service.SomeService" check="false" cluster="failfast"/>
這樣,在服務(wù)調(diào)用出現(xiàn)異常時(shí),Dubbo框架會(huì)自動(dòng)使用Failfast容錯(cuò)機(jī)制進(jìn)行處理,即只進(jìn)行一次調(diào)用,若調(diào)用失敗則立即拋出異常,不進(jìn)行重試。
3、自定義容錯(cuò)策略的實(shí)現(xiàn)示例
如果需要實(shí)現(xiàn)自定義的容錯(cuò)策略,可以通過繼承org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker?類,并實(shí)現(xiàn)org.apache.dubbo.rpc.Invoker接口,來自定義容錯(cuò)策略的實(shí)現(xiàn)。
例如,我們可以通過以下代碼實(shí)現(xiàn)一個(gè)自定義的容錯(cuò)策略:
public class MyClusterInvoker<T> extends AbstractClusterInvoker<T> {
public MyClusterInvoker(Directory<T> directory) {
super(directory);
}
@Override
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
// 自定義容錯(cuò)邏輯
...
}
}
在實(shí)現(xiàn)自定義容錯(cuò)策略后,需要在服務(wù)提供方和服務(wù)消費(fèi)方的配置文件中進(jìn)行配置。
例如,在服務(wù)提供方的配置文件中增加如下配置:
<dubbo:service interface="com.example.service.SomeService" cluster="myClusterInvoker"/>
在服務(wù)消費(fèi)方的配置文件中增加如下配置:
<dubbo:reference interface="com.example.service.SomeService" check="false" cluster="myClusterInvoker"/>
這樣,在服務(wù)調(diào)用時(shí),Dubbo框架會(huì)使用我們自定義的MyClusterInvoker容錯(cuò)策略進(jìn)行處理。
八、Dubbo的擴(kuò)展機(jī)制實(shí)踐
1、實(shí)現(xiàn)一個(gè)使用自定義擴(kuò)展點(diǎn)、過濾器、負(fù)載均衡器和容錯(cuò)機(jī)制的 Dubbo 服務(wù)
在這個(gè)實(shí)踐中,我們將實(shí)現(xiàn)一個(gè)使用自定義擴(kuò)展點(diǎn)、過濾器、負(fù)載均衡器和容錯(cuò)機(jī)制的 Dubbo 服務(wù)。
2、首先,我們需要定義一個(gè)服務(wù)接口。
例如,我們可以定義一個(gè)名為 SomeService 的服務(wù)接口,如下所示:
public interface SomeService {
String sayHello(String name);
}
3、然后,我們需要實(shí)現(xiàn)該服務(wù)接口。
例如,我們可以實(shí)現(xiàn)一個(gè)名為 SomeServiceImpl 的服務(wù)實(shí)現(xiàn)類,如下所示:
public class SomeServiceImpl implements SomeService {
@Override
public String sayHello(String name) {
return "Hello, " + name + "!";
}
}
4、接下來,我們需要配置 Dubbo 的擴(kuò)展點(diǎn)、過濾器、負(fù)載均衡器和容錯(cuò)機(jī)制。
例如,我們可以在服務(wù)提供方和服務(wù)消費(fèi)方的配置文件中進(jìn)行如下配置:
<!-- 擴(kuò)展點(diǎn)配置 -->
<dubbo:protocol name="dubbo" extensinotallow="com.example.extension.MyProtocol"/>
<!-- 過濾器配置 -->
<dubbo:provider filter="com.example.filter.MyProviderFilter"/>
<dubbo:consumer filter="com.example.filter.MyConsumerFilter"/>
<!-- 負(fù)載均衡器配置 -->
<dubbo:reference interface="com.example.service.SomeService" loadbalance="com.example.loadbalance.MyLoadBalance"/>
<!-- 容錯(cuò)機(jī)制配置 -->
<dubbo:service interface="com.example.service.SomeService" cluster="com.example.cluster.MyCluster"/>
<dubbo:reference interface="com.example.service.SomeService" cluster="com.example.cluster.MyCluster"/>
其中,com.example.extension.MyProtocol? 是一個(gè)自定義的 Dubbo 協(xié)議擴(kuò)展點(diǎn)實(shí)現(xiàn)類,com.example.filter.MyProviderFilter? 和 com.example.filter.MyConsumerFilter? 是自定義的 Dubbo 過濾器實(shí)現(xiàn)類,com.example.loadbalance.MyLoadBalance? 是一個(gè)自定義的 Dubbo 負(fù)載均衡器實(shí)現(xiàn)類,com.example.cluster.MyCluster 是一個(gè)自定義的 Dubbo 容錯(cuò)機(jī)制實(shí)現(xiàn)類。
5、最后,我們可以使用 Dubbo 的 API 在客戶端調(diào)用該服務(wù)。
例如,我們可以使用如下代碼在客戶端調(diào)用該服務(wù):
// 獲取 Dubbo 服務(wù)引用
SomeService someService = DubboReferenceBuilder.newBuilder()
.setInterface(SomeService.class)
.setUrl("dubbo://localhost:20880")
.build();
// 調(diào)用 Dubbo 服務(wù)
String result = someService.sayHello("Dubbo");
System.out.println(result);
這樣,我們就實(shí)現(xiàn)了一個(gè)使用自定義擴(kuò)展點(diǎn)、過濾器、負(fù)載均衡器和容錯(cuò)機(jī)制的 Dubbo 服務(wù)。
本文轉(zhuǎn)載自微信公眾號「哪吒編程」