jdk SPI與dubbo SPI

1.jdk SPI介紹


SPI 全稱(chēng)為 Service Provider Interface冯乘,是一種服務(wù)發(fā)現(xiàn)機(jī)制恕洲。SPI 的\color{red}{本質(zhì)是將接口實(shí)現(xiàn)類(lèi)的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件图焰,加載實(shí)現(xiàn)類(lèi)启盛。}這樣可以在\color{red}{運(yùn)行時(shí),動(dòng)態(tài)為接口替換實(shí)現(xiàn)類(lèi)技羔。正因此特性}僵闯,我們可以很容易的通過(guò) SPI 機(jī)制為我們的程序提供拓展功能。SPI 機(jī)制在第三方框架中也有所應(yīng)用藤滥,比如 Dubbo 就是通過(guò) SPI 機(jī)制加載所有的組件鳖粟。不過(guò),Dubbo 并未使用 Java 原生的 SPI 機(jī)制拙绊,而是對(duì)其進(jìn)行了增強(qiáng)向图,使其能夠更好的滿(mǎn)足需求。在 Dubbo 中标沪,SPI 是一個(gè)非常重要的模塊榄攀。基于 SPI金句,我們可以很容易的對(duì) Dubbo 進(jìn)行拓展檩赢。如果大家想要學(xué)習(xí) Dubbo 的源碼,SPI 機(jī)制務(wù)必弄懂违寞。接下來(lái)贞瞒,我們先來(lái)了解一下 Java SPI 與 Dubbo SPI 的用法,然后再來(lái)分析 Dubbo SPI 的源碼趁曼。以上來(lái)自dubbo官方文檔

我們系統(tǒng)里抽象的各個(gè)模塊军浆,往往有很多不同的實(shí)現(xiàn)方案,比如日志模塊的方案挡闰,xml解析模塊瘾敢、jdbc模塊的方案等。面向的對(duì)象的設(shè)計(jì)里尿这,我們一般推薦模塊之間基于接口編程簇抵,模塊之間不對(duì)實(shí)現(xiàn)類(lèi)進(jìn)行硬編碼。一旦代碼里涉及具體的實(shí)現(xiàn)類(lèi)射众,就違反了可拔插的原則碟摆,如果需要替換一種實(shí)現(xiàn),就需要修改代碼叨橱。為了實(shí)現(xiàn)在模塊裝配的時(shí)候能不在程序里動(dòng)態(tài)指明典蜕,這就需要一種服務(wù)發(fā)現(xiàn)機(jī)制断盛。 java spi就是提供這樣的一個(gè)機(jī)制:為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。有點(diǎn)類(lèi)似IOC的思想愉舔,就是將裝配的控制權(quán)移到程序之外钢猛,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要。

SPI的具體約定:服務(wù)的提供者轩缤,提供了服務(wù)接口的一種實(shí)現(xiàn)之后命迈,在jar包的META-INF/services/目錄里同時(shí)創(chuàng)建一個(gè)以服務(wù)接口命名的文件。該文件里就是實(shí)現(xiàn)該服務(wù)接口的具體實(shí)現(xiàn)類(lèi)火的。而當(dāng)外部程序裝配這個(gè)模塊的時(shí)候壶愤,就能通過(guò)該jar包META-INF/services/里的配置文件找到具體的實(shí)現(xiàn)類(lèi)名,并裝載實(shí)例化馏鹤,完成模塊的注入征椒。 基于這樣一個(gè)約定就能很好的找到服務(wù)接口的實(shí)現(xiàn)類(lèi),而不需要再代碼里制定湃累。jdk提供服務(wù)實(shí)現(xiàn)查找的一個(gè)工具類(lèi):java.util.ServiceLoader

  • common-logging

apache最早提供的日志的門(mén)面接口勃救。只有接口,沒(méi)有實(shí)現(xiàn)治力。具體方案由各提供商實(shí)現(xiàn)剪芥, 發(fā)現(xiàn)日志提供商是通過(guò)掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通過(guò)讀取該文件的內(nèi)容找到日志提工商實(shí)現(xiàn)類(lèi)琴许。只要我們的日志實(shí)現(xiàn)里包含了這個(gè)文件,并在文件里制定 LogFactory工廠(chǎng)接口的實(shí)現(xiàn)類(lèi)即可溉躲。

  • JDBC

jdbc4.0以前榜田, 開(kāi)發(fā)人員還需要基于Class.forName("xxx")的方式來(lái)裝載驅(qū)動(dòng),jdbc4也基于spi的機(jī)制來(lái)發(fā)現(xiàn)驅(qū)動(dòng)提供商了锻梳,可以通過(guò)META-INF/services/java.sql.Driver文件里指定實(shí)現(xiàn)類(lèi)的方式來(lái)暴露驅(qū)動(dòng)提供者.

jdbc連接過(guò)程 賈璉預(yù)執(zhí)事(加載驅(qū)動(dòng)箭券、鏈接數(shù)據(jù)庫(kù)、預(yù)執(zhí)行疑枯、執(zhí)行辩块、釋放資源)

//加載JDBC驅(qū)動(dòng)程序
Class.forName("com.mysql.jdbc.Driver") ; 
//2、提供JDBC連接的URL
String url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
//3荆永、創(chuàng)建數(shù)據(jù)庫(kù)的連接
Connection con =  DriverManager.getConnection(url , username , password ) ; 
//4废亭、創(chuàng)建一個(gè)Statement
PreparedStatement pstmt = con.prepareStatement(sql) ;   
//5、執(zhí)行SQL語(yǔ)句
ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ;   
 while(rs.next()){   
         //do something
     }  
//6具钥、釋放資源
con.close()

我們都知道豆村,也聽(tīng)了無(wú)數(shù)遍,驅(qū)動(dòng)的加載是由Class.forName 方法完成的骂删。
由于JVM對(duì)類(lèi)的加載有一個(gè)邏輯是:在類(lèi)被需要的時(shí)候掌动,或者首次調(diào)用的時(shí)候就會(huì)把類(lèi)加載到JVM四啰。反過(guò)來(lái)也就是:如果類(lèi)沒(méi)有被需要的時(shí)候,一般是不會(huì)被加載到JVM的粗恢。

當(dāng)連接數(shù)據(jù)庫(kù)的時(shí)候我們調(diào)用了Class.forName語(yǔ)句之后柑晒,數(shù)據(jù)庫(kù)驅(qū)動(dòng)類(lèi)被加載到JVM,那么靜態(tài)初始化塊就會(huì)被執(zhí)行眷射,從而完成驅(qū)動(dòng)的注冊(cè)工作匙赞,也就是注冊(cè)到了JDBC的DriverManager類(lèi)中。

由于是靜態(tài)初始化塊中完成的加載凭迹,所以也就不必?fù)?dān)心驅(qū)動(dòng)被加載多次罚屋,原因可以參考單例模式相關(guān)的知識(shí)。

拋棄Class.forName()

在JDBC 4.0之后實(shí)際上我們不需要再調(diào)用Class.forName來(lái)加載驅(qū)動(dòng)程序了嗅绸,我們只需要把驅(qū)動(dòng)的jar包放到工程的類(lèi)加載路徑里脾猛,那么驅(qū)動(dòng)就會(huì)被自動(dòng)加載。

這個(gè)自動(dòng)加載采用的技術(shù)叫做SPI鱼鸠,數(shù)據(jù)庫(kù)驅(qū)動(dòng)廠(chǎng)商也都做了更新猛拴。可以看一下jar包里面的META-INF/services目錄蚀狰,里面有一個(gè)java.sql.Driver的文件愉昆,文件里面包含了驅(qū)動(dòng)的全路徑名。

比如mysql-connector里面的內(nèi)容:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

那么SPI技術(shù)又是在什么階段加載的數(shù)據(jù)庫(kù)驅(qū)動(dòng)呢麻蹋?看一下JDBC的DriverManager類(lèi)就知道了跛溉。

public class DriverManager {
    static {
        loadInitialDrivers();//......1
        println("JDBC DriverManager initialized");
    }

    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                 return System.getProperty("jdbc.drivers");
                }
                });
           } catch (Exception ex) {
                drivers = null;
           }

           AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);//.....2
                   Iterator driversIterator = loadedDrivers.iterator();

                //.....
}

上述代碼片段標(biāo)記…1的位置是在DriverManager類(lèi)加載是執(zhí)行的靜態(tài)初始化塊,這里會(huì)調(diào)用loadInitialDrivers方法扮授。

再看loadInitialDrivers方法里面標(biāo)記…2的位置芳室,這里調(diào)用的 ServiceLoader.load(Driver.class); 就會(huì)加載所有在META-INF/services/java.sql.Driver文件里邊的類(lèi)到JVM內(nèi)存,完成驅(qū)動(dòng)的自動(dòng)加載刹勃。

這就是SPI的優(yōu)勢(shì)所在堪侯,能夠自動(dòng)的加載類(lèi)到JVM內(nèi)存。這個(gè)技術(shù)在阿里的dubbo框架里面也占到了很大的分量荔仁,有興趣的朋友可以看一下dubbo的代碼伍宦,或者百度一下dubbo的擴(kuò)展機(jī)制。

JDBC如何區(qū)分多個(gè)驅(qū)動(dòng)乏梁?

一個(gè)項(xiàng)目里邊很可能會(huì)即連接MySQL次洼,又連接Oracle,這樣在一個(gè)工程里邊就存在了多個(gè)驅(qū)動(dòng)類(lèi)遇骑,那么這些驅(qū)動(dòng)類(lèi)又是怎么區(qū)分的呢滓玖?

關(guān)鍵點(diǎn)就在于getConnection的步驟,DriverManager.getConnection中會(huì)遍歷所有已經(jīng)加載的驅(qū)動(dòng)實(shí)例去創(chuàng)建連接质蕉,當(dāng)一個(gè)驅(qū)動(dòng)創(chuàng)建連接成功時(shí)就會(huì)返回這個(gè)連接势篡,同時(shí)不再調(diào)用其他的驅(qū)動(dòng)實(shí)例翩肌。DriverManager關(guān)鍵代碼如下:

private static Connection getConnection(
    //.....

    for(DriverInfo aDriver : registeredDrivers) {
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                println("    trying " + aDriver.driver.getClass().getName());
                Connection con = aDriver.driver.connect(url, info);
                if (con != null) {
                    // Success!
                    println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                }
            } catch (SQLException ex) {
                if (reason == null) {
                      reason = ex;
                }
            }
        } else {
            println("    skipping: " + aDriver.getClass().getName());
        }
     }

是不是每個(gè)驅(qū)動(dòng)實(shí)例都真真實(shí)實(shí)的要嘗試建立連接呢?不是的禁悠!

\color{red}{每個(gè)驅(qū)動(dòng)實(shí)例在getConnetion的第一步就是按照url判斷是不是符合自己的處理規(guī)則念祭,是的話(huà)才會(huì)和db建立連接。}比如碍侦,MySQL驅(qū)動(dòng)類(lèi)中的關(guān)鍵代碼:

public boolean acceptsURL(String url) throws SQLException {
        return (parseURL(url, null) != null);
    }

    public Properties parseURL(String url, Properties defaults)
            throws java.sql.SQLException {
        Properties urlProps = (defaults != null) ? new Properties(defaults)
                : new Properties();

        if (url == null) {
            return null;
        }

        if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX)
                && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)
                && !StringUtils.startsWithIgnoreCase(url,
                        LOADBALANCE_URL_PREFIX)
                && !StringUtils.startsWithIgnoreCase(url,
                        REPLICATION_URL_PREFIX)) { //$NON-NLS-1$

            return null;
        }
        //......

2.SPI示例

3.JDK SPI缺點(diǎn)

  • 需要遍歷所有的實(shí)現(xiàn)粱坤,并實(shí)例化,然后我們?cè)谘h(huán)中才能找到我們需要的實(shí)現(xiàn)瓷产。
  • 配置文件中只是簡(jiǎn)單的列出了所有的擴(kuò)展實(shí)現(xiàn)站玄,而沒(méi)有給他們命名。導(dǎo)致在程序中很難去準(zhǔn)確的引用它們濒旦。
  • 擴(kuò)展如果依賴(lài)其他的擴(kuò)展株旷,做不到自動(dòng)注入和裝配
  • 不提供類(lèi)似于Spring的AOP功能
  • 擴(kuò)展很難和其他的框架集成,比如擴(kuò)展里面依賴(lài)了一個(gè)Spring bean尔邓,原生的Java SPI不支持
    所以Java SPI應(yīng)付一些簡(jiǎn)單的場(chǎng)景是可以的晾剖,但對(duì)于Dubbo华临,它的功能還是比較弱的崖叫。Dubbo對(duì)原生SPI機(jī)制進(jìn)行了一些擴(kuò)展。接下來(lái)亮元,我們就更深入地了解下Dubbo的SPI機(jī)制灯节。

4.dubbo SPI

擴(kuò)展點(diǎn)(Extension Point)
是一個(gè)Java的接口循头。
擴(kuò)展(Extension)
擴(kuò)展點(diǎn)的實(shí)現(xiàn)類(lèi)。
擴(kuò)展實(shí)例
擴(kuò)展點(diǎn)實(shí)現(xiàn)類(lèi)的實(shí)例炎疆。
擴(kuò)展自適應(yīng)實(shí)例(Extension Adaptive Instance)
第一次接觸這個(gè)概念時(shí)卡骂,可能不太好理解(我第一次也是這樣的...)。如果稱(chēng)它為擴(kuò)展代理類(lèi)磷雇,可能更好理解些。擴(kuò)展的自適應(yīng)實(shí)例其實(shí)就是一個(gè)Extension的代理躏救,它實(shí)現(xiàn)了擴(kuò)展點(diǎn)接口唯笙。在調(diào)用擴(kuò)展點(diǎn)的接口方法時(shí),會(huì)根據(jù)實(shí)際的參數(shù)來(lái)決定要使用哪個(gè)擴(kuò)展盒使。比如一個(gè)IRepository的擴(kuò)展點(diǎn)崩掘,有一個(gè)save方法。有兩個(gè)實(shí)現(xiàn)MysqlRepository和MongoRepository少办。IRepository的自適應(yīng)實(shí)例在調(diào)用接口方法的時(shí)候苞慢,會(huì)根據(jù)save方法中的參數(shù),來(lái)決定要調(diào)用哪個(gè)IRepository的實(shí)現(xiàn)英妓。如果方法參數(shù)中有repository=mysql挽放,那么就調(diào)用MysqlRepository的save方法绍赛。如果repository=mongo,就調(diào)用MongoRepository的save方法辑畦。和面向?qū)ο蟮难舆t綁定很類(lèi)似吗蚌。為什么Dubbo會(huì)引入擴(kuò)展自適應(yīng)實(shí)例的概念呢?

Dubbo中的配置有兩種纯出,一種是固定的系統(tǒng)級(jí)別的配置蚯妇,在Dubbo啟動(dòng)之后就不會(huì)再改了。還有一種是運(yùn)行時(shí)的配置暂筝,可能對(duì)于每一次的RPC箩言,這些配置都不同。比如在xml文件中配置了超時(shí)時(shí)間是10秒鐘焕襟,這個(gè)配置在Dubbo啟動(dòng)之后陨收,就不會(huì)改變了。但針對(duì)某一次的RPC調(diào)用胧洒,可以設(shè)置它的超時(shí)時(shí)間是30秒鐘畏吓,以覆蓋系統(tǒng)級(jí)別的配置。對(duì)于Dubbo而言卫漫,每一次的RPC調(diào)用的參數(shù)都是未知的菲饼。只有在運(yùn)行時(shí),根據(jù)這些參數(shù)才能做出正確的決定列赎。
很多時(shí)候宏悦,我們的類(lèi)都是一個(gè)單例的,比如Spring的bean包吝,在Spring bean都實(shí)例化時(shí)饼煞,如果它依賴(lài)某個(gè)擴(kuò)展點(diǎn),但是在bean實(shí)例化時(shí)诗越,是不知道究竟該使用哪個(gè)具體的擴(kuò)展實(shí)現(xiàn)的砖瞧。這時(shí)候就需要一個(gè)代理模式了,它實(shí)現(xiàn)了擴(kuò)展點(diǎn)接口嚷狞,方法內(nèi)部可以根據(jù)運(yùn)行時(shí)參數(shù)块促,動(dòng)態(tài)的選擇合適的擴(kuò)展實(shí)現(xiàn)。而這個(gè)代理就是自適應(yīng)實(shí)例床未。 自適應(yīng)擴(kuò)展實(shí)例在Dubbo中的使用非常廣泛竭翠,Dubbo中,每一個(gè)擴(kuò)展都會(huì)有一個(gè)自適應(yīng)類(lèi)薇搁,如果我們沒(méi)有提供斋扰,Dubbo會(huì)使用字節(jié)碼工具為我們自動(dòng)生成一個(gè)。所以我們基本感覺(jué)不到自適應(yīng)類(lèi)的存在。后面會(huì)有例子說(shuō)明自適應(yīng)類(lèi)是怎么工作的传货。
@SPI
@SPI注解作用于擴(kuò)展點(diǎn)的接口上屎鳍,表明該接口是一個(gè)擴(kuò)展點(diǎn)∷鹄耄可以被Dubbo的ExtentionLoader加載哥艇。如果沒(méi)有此ExtensionLoader調(diào)用會(huì)異常。
@Adaptive
@Adaptive注解用在擴(kuò)展接口的方法上僻澎。表示該方法是一個(gè)自適應(yīng)方法貌踏。Dubbo在為擴(kuò)展點(diǎn)生成自適應(yīng)實(shí)例時(shí),如果方法有@Adaptive注解窟勃,會(huì)為該方法生成對(duì)應(yīng)的代碼祖乳。方法內(nèi)部會(huì)根據(jù)方法的參數(shù),來(lái)決定使用哪個(gè)擴(kuò)展秉氧。
ExtentionLoader
類(lèi)似于Java SPI的ServiceLoader眷昆,負(fù)責(zé)擴(kuò)展的加載和生命周期維護(hù)。
擴(kuò)展別名
和Java SPI不同汁咏,Dubbo中的擴(kuò)展都有一個(gè)別名亚斋,用于在應(yīng)用中引用它們。比如

random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance 
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance 

其中的random攘滩,roundrobin就是對(duì)應(yīng)擴(kuò)展的別名帅刊。這樣我們?cè)谂渲梦募惺褂胷andom或roundrobin就可以了。
路徑
和Java SPI從/META-INF/services目錄加載擴(kuò)展配置類(lèi)似漂问,Dubbo也會(huì)從以下路徑去加載擴(kuò)展配置文件:

  • META-INF/dubbo/internal
  • META-INF/dubbo
  • META-INF/services

5.Dubbo的LoadBalance擴(kuò)展點(diǎn)解讀

在了解了Dubbo的一些基本概念后赖瞒,讓我們一起來(lái)看一個(gè)Dubbo中實(shí)際的擴(kuò)展點(diǎn),對(duì)這些概念有一個(gè)更直觀(guān)的認(rèn)識(shí)蚤假。
我們選擇的是Dubbo中的\color{red}{LoadBalance}擴(kuò)展點(diǎn)栏饮。Dubbo中的一個(gè)服務(wù),通常有多個(gè)Provider磷仰,consumer調(diào)用服務(wù)時(shí)袍嬉,需要在多個(gè)Provider中選擇一個(gè)。這就是一個(gè)LoadBalance灶平。我們一起來(lái)看看在Dubbo中伺通,LoadBalance是如何成為一個(gè)擴(kuò)展點(diǎn)的。

package com.alibaba.dubbo.rpc.cluster;
@SPI("random")
public interface LoadBalance {
    @Adaptive({"loadbalance"})
    <T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException;
}

LoadBalance接口只有一個(gè)select方法民逼。select方法從多個(gè)invoker中選擇其中一個(gè)泵殴。上面代碼中和Dubbo SPI相關(guān)的元素有:

  • @SPI("random") @SPI作用于LoadBalance接口涮帘,表示接口LoadBalance是一個(gè)擴(kuò)展點(diǎn)拼苍。如果沒(méi)有@SPI注解,試圖去加載擴(kuò)展時(shí),會(huì)拋出異常疮鲫。@SPI注解有一個(gè)參數(shù)吆你,該參數(shù)表示該擴(kuò)展點(diǎn)的默認(rèn)實(shí)現(xiàn)的別名。如果沒(méi)有顯示的指定擴(kuò)展俊犯,就使用默認(rèn)實(shí)現(xiàn)妇多。默認(rèn)實(shí)現(xiàn)是"random",是一個(gè)隨機(jī)負(fù)載均衡的實(shí)現(xiàn)燕侠。 random的定義在配置文件META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.LoadBalance中:
random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance 
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance 
leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance 
consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance

可以看到文件中定義了4個(gè)LoadBalance的擴(kuò)展實(shí)現(xiàn)者祖。由于負(fù)載均衡的實(shí)現(xiàn)不是本次的內(nèi)容,這里就不過(guò)多說(shuō)明绢彤。只用知道Dubbo提供了4種負(fù)載均衡的實(shí)現(xiàn)七问,我們可以通過(guò)xml文件,properties文件茫舶,JVM參數(shù)顯式的指定一個(gè)實(shí)現(xiàn)械巡。如果沒(méi)有,默認(rèn)使用隨機(jī)饶氏。

  • @Adaptive("loadbalance")@Adaptive注解修飾select方法讥耗,表明方法select方法是一個(gè)可自適應(yīng)的方法。Dubbo會(huì)自動(dòng)生成該方法對(duì)應(yīng)的代碼疹启。當(dāng)調(diào)用select方法時(shí)古程,會(huì)根據(jù)具體的方法參數(shù)來(lái)決定調(diào)用哪個(gè)擴(kuò)展實(shí)現(xiàn)的select方法。@Adaptive注解的參數(shù)loadbalance表示方法參數(shù)中的loadbalance的值作為實(shí)際要調(diào)用的擴(kuò)展實(shí)例皮仁。 但奇怪的是籍琳,我們發(fā)現(xiàn)select的方法中并沒(méi)有l(wèi)oadbalance參數(shù),那怎么獲取loadbalance的值呢贷祈?select方法中還有一個(gè)URL類(lèi)型的參數(shù)趋急,Dubbo就是從URL中獲取loadbalance的值的。這里涉及到Dubbo的URL總線(xiàn)模式势誊,簡(jiǎn)單說(shuō)呜达,URL中包含了RPC調(diào)用中的所有參數(shù)。URL類(lèi)中有一個(gè)Map \color{red}{parameters}字段粟耻,parameters中就包含了loadbalance查近。
LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName); 

使用ExtensionLoader.getExtensionLoader(LoadBalance.class)方法獲取一個(gè)ExtensionLoader的實(shí)例,然后調(diào)用getExtension挤忙,傳入一個(gè)擴(kuò)展的別名來(lái)獲取對(duì)應(yīng)的擴(kuò)展實(shí)例霜威。

6.自定義一個(gè)LoadBalance擴(kuò)展

1.實(shí)現(xiàn)LoadBalance接口,首先,編寫(xiě)一個(gè)自己實(shí)現(xiàn)的LoadBalance册烈,因?yàn)槭菫榱搜菔綝ubbo的擴(kuò)展機(jī)制戈泼,而不是LoadBalance的實(shí)現(xiàn),所以這里L(fēng)oadBalance的實(shí)現(xiàn)非常簡(jiǎn)單,選擇第一個(gè)invoker大猛,并在控制臺(tái)輸出一條日志扭倾。

public class DemoLoadBalance implements LoadBalance {
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        System.out.println("DemoLoadBalance : Select the first invoker...");
        return invokers.get(0);
    }
}

2.META-INF/dubbo下添加擴(kuò)展配置文件

demo=com.dubbo.spi.demo.consumer.DemoLoadBalance
  1. 通過(guò)上面的兩步,已經(jīng)添加了一個(gè)名字為demo的LoadBalance實(shí)現(xiàn)挽绩,并在配置文件中進(jìn)行了相應(yīng)的配置膛壹。接下來(lái),需要顯式的告訴Dubbo使用demo的負(fù)載均衡實(shí)現(xiàn)唉堪。如果是通過(guò)spring的方式使用Dubbo模聋,可以在xml文件中進(jìn)行設(shè)置。
 <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
 <dubbo:reference id="helloService" interface="com.dubbo.spi.demo.api.IHelloService" loadbalance="demo" />

啟動(dòng)Dubbo
啟動(dòng)Dubbo唠亚,調(diào)用一次IHelloService撬槽,可以看到控制臺(tái)會(huì)輸出一條DemoLoadBalance: Select the first invoker...日志。說(shuō)明Dubbo的確是使用了我們自定義的LoadBalance趾撵。

zk

image.png

控制臺(tái)

服務(wù)提供者1

服務(wù)提供者2

總結(jié)dubbo SPI

  • 對(duì)Dubbo進(jìn)行擴(kuò)展侄柔,不需要改動(dòng)Dubbo的源碼
  • 自定義的Dubbo的擴(kuò)展點(diǎn)實(shí)現(xiàn),是一個(gè)普通的Java類(lèi)占调,Dubbo沒(méi)有引入任何Dubbo特有的元素暂题,對(duì)代碼侵入性幾乎為零。
  • 將擴(kuò)展注冊(cè)到Dubbo中究珊,只需要在ClassPath中添加配置文件薪者。使用簡(jiǎn)單。而且不會(huì)對(duì)現(xiàn)有代碼造成影響剿涮。符合開(kāi)閉原則言津。
  • Dubbo的擴(kuò)展機(jī)制支持IoC,AoP等高級(jí)功能
  • Dubbo的擴(kuò)展機(jī)制能很好的支持第三方IOC容器,默認(rèn)支持Spring Bean取试,可自己擴(kuò)展來(lái)支持其他容器悬槽,比如Google的Guice。
  • 切換擴(kuò)展點(diǎn)的實(shí)現(xiàn)瞬浓,只需要在配置文件中修改具體的實(shí)現(xiàn)初婆,不需要改代碼。使用方便猿棉。

轉(zhuǎn)載鏈接
轉(zhuǎn)載鏈接
dubbo各協(xié)議比較

Dubbo作者親述:那些輝煌磅叛、沉寂與重生的故事

dubbo官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市萨赁,隨后出現(xiàn)的幾起案子弊琴,更是在濱河造成了極大的恐慌,老刑警劉巖杖爽,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敲董,死亡現(xiàn)場(chǎng)離奇詭異详瑞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)臣缀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)泻帮,“玉大人精置,你說(shuō)我怎么就攤上這事÷嘣樱” “怎么了脂倦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)元莫。 經(jīng)常有香客問(wèn)我赖阻,道長(zhǎng),這世上最難降的妖魔是什么踱蠢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任火欧,我火速辦了婚禮,結(jié)果婚禮上茎截,老公的妹妹穿的比我還像新娘苇侵。我一直安慰自己,他們只是感情好企锌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布榆浓。 她就那樣靜靜地躺著,像睡著了一般撕攒。 火紅的嫁衣襯著肌膚如雪陡鹃。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天抖坪,我揣著相機(jī)與錄音萍鲸,去河邊找鬼。 笑死擦俐,一個(gè)胖子當(dāng)著我的面吹牛猿推,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捌肴,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蹬叭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了状知?” 一聲冷哼從身側(cè)響起秽五,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎饥悴,沒(méi)想到半個(gè)月后坦喘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盲再,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年瓣铣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了答朋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棠笑,死狀恐怖梦碗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蓖救,我是刑警寧澤洪规,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站循捺,受9級(jí)特大地震影響斩例,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜从橘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一念赶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恰力,春花似錦晶乔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至驻民,卻和暖如春翻具,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背回还。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工裆泳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柠硕。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓工禾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蝗柔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闻葵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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

  • 從今天開(kāi)始,將會(huì)逐步介紹關(guān)于DUbbo的有關(guān)知識(shí)癣丧。首先先簡(jiǎn)單介紹一下DUbbo的整體概述槽畔。 概述 Dubbo是SO...
    九點(diǎn)半的馬拉閱讀 1,363評(píng)論 0 0
  • 文章要點(diǎn) 什么是spi dubbo為什么實(shí)現(xiàn)自己的spi dubbo的adaptive機(jī)制 dubbo的IOC和A...
    青芒v5閱讀 684評(píng)論 0 4
  • 0 前言 站在一個(gè)框架作者的角度來(lái)說(shuō),定義一個(gè)接口胁编,自己默認(rèn)給出幾個(gè)接口的實(shí)現(xiàn)類(lèi)厢钧,同時(shí) 允許框架的使用者也能夠自定...
    七寸知架構(gòu)閱讀 16,243評(píng)論 3 67
  • 在 Dubbo 中鳞尔,SPI 貫穿在整個(gè) Dubbo 的核心,所以把 Dubbo 里面用得比較多的 SPI 機(jī)制做一...
    WEIJAVA閱讀 484評(píng)論 0 1
  • 站在十字街頭早直,不知向何方邁出腳步寥假, 細(xì)雨落在頭頂肩頭慢慢浸濕發(fā)絲與薄衫, 皮膚對(duì)雨的觸感使她微微顫栗霞扬, 這是夏日的...
    小雨的海閱讀 489評(píng)論 7 10