成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Dubbo服務(wù)提供者發(fā)布過程

lifesimple / 1790人閱讀

摘要:將標記為服務(wù),使用對象來提供具體的服務(wù)。這整個過程算是該類的典型的執(zhí)行過程。從上面得知服務(wù)發(fā)布的第一二個過程獲取注冊中心信息和協(xié)議信息。對于端來說,上述服務(wù)發(fā)布的第步中要解決的問題是根據(jù)指定協(xié)議向注冊中心注冊服務(wù)。

上圖是服務(wù)提供者暴露服務(wù)的主過程

首先ServiceConfig類拿到對外提供服務(wù)的實際類ref(如:HelloServiceImpl),然后通過ProxyFactory類的getInvoker方法使用ref生成一個AbstractProxyInvoker實例,到這一步就完成具體服務(wù)到Invoker的轉(zhuǎn)化。接下來就是Invoker轉(zhuǎn)換到Exporter的過程。

Dubbo處理服務(wù)暴露的關(guān)鍵就在Invoker轉(zhuǎn)換到Exporter的過程(如上圖中的紅色部分),Dubbo協(xié)議的Invoker轉(zhuǎn)為Exporter發(fā)生在DubboProtocol類的export方法,它主要是打開socket偵聽服務(wù),并接收客戶端發(fā)來的各種請求,通訊細節(jié)由Dubbo自己實現(xiàn).

服務(wù)發(fā)布過程大致分成3步

1、獲取注冊中心信息,構(gòu)建協(xié)議信息,然后將其組合。
2、通過ProxyFactory將HelloServiceImpl封裝成一個Invoker執(zhí)行 。
3、使用Protocol將invoker導(dǎo)出成一個Exporter(包括去注冊中心注冊服務(wù)等)。

這里面就涉及到幾個大的概念,ProxyFactory、Invoker、Protocol、Exporter

Export 服務(wù)暴露的步驟

1、首先會檢查各種配置信息、 等標簽的配置,填充各種屬性,總之就是保證我在開始暴露服務(wù)之前,所有的東西都準備好了,并且是正確的。
2、加載所有的注冊中心,因為我們暴露服務(wù)需要注冊到注冊中心中去。
3、根據(jù)配置的所有協(xié)議和注冊中心url分別進行導(dǎo)出。
4、進行導(dǎo)出的時候,又是一波屬性的獲取設(shè)置檢查等操作。
5、如果配置的不是remote,則做本地導(dǎo)出。
6、如果配置的不是local,則暴露為遠程服務(wù)。
7、不管是本地還是遠程服務(wù)暴露,首先都會獲取Invoker。
8、獲取完Invoker之后,轉(zhuǎn)換成對外的Exporter,緩存起來。
9、執(zhí)行DubboProtocol類的export方法,打開socket偵聽服務(wù),并接收客戶端發(fā)來的各種請求。

概念介紹

先看一個簡單的服務(wù)端例子,dubbo配置如下:





有一個服務(wù)接口HelloService,以及它對應(yīng)的實現(xiàn)類HelloServiceImpl。

將HelloService標記為dubbo服務(wù),使用HelloServiceImpl對象來提供具體的服務(wù)。

使用zooKeeper作為注冊中心。

Invoker

Invoker,一個可執(zhí)行對象,能夠根據(jù)方法名稱、參數(shù)得到相應(yīng)的執(zhí)行結(jié)果。接口如下:

public interface Invoker {

    Class getInterface();

    URL getUrl();

    Result invoke(Invocation invocation) throws RpcException;

    void destroy();

}

而Invocation則包含了需要執(zhí)行的方法、參數(shù)等信息,接口如下:

public interface Invocation {

    URL getUrl();

    String getMethodName();

    Class[] getParameterTypes();

    Object[] getArguments();

}

目前其實現(xiàn)類只有一個RpcInvocation

Invoker這個可執(zhí)行對象的執(zhí)行過程分成三種類型:

本地執(zhí)行的Invoker

遠程通信執(zhí)行的Invoker

多個類型2的Invoker聚合成的集群版Invoker

以HelloService接口為例:

本地執(zhí)行的Invokerserver端,含有對應(yīng)的HelloServiceImpl實現(xiàn),要執(zhí)行該接口方法,僅僅只需要通過反射執(zhí)行HelloServiceImpl對應(yīng)的方法即可。

遠程通信執(zhí)行的Invokerclient端,要想執(zhí)行該接口方法,需要需要進行遠程通信,發(fā)送要執(zhí)行的參數(shù)信息給server端;server端,利用上述本地執(zhí)行的Invoker執(zhí)行相應(yīng)的方法,然后將返回的結(jié)果發(fā)送給client端。這整個過程算是該類Invoker的典型的執(zhí)行過程。

集群版的Invokerclient端,擁有某個服務(wù)的多個Invoker,此時client端需要做的就是將多個Invoker聚合成一個集群版的Invoker,client端使用的時候,僅僅通過集群版的Invoker來進行操作。集群版的Invoker會從眾多的遠程通信類型的Invoker中選擇一個來執(zhí)行(從中加入負載均衡、服務(wù)降級等策略),類似服務(wù)治理,dubbo已經(jīng)實現(xiàn)了、

看下Invoker的實現(xiàn)情況:

ProxyFactory

對于Server端,主要負責(zé)將服務(wù)如HelloServiceImpl統(tǒng)一進行包裝成一個Invoker,通過反射來執(zhí)行具體的HelloServiceImpl對象的方法

接口定義如下:

@SPI("javassist")
public interface ProxyFactory {

     //針對client端,創(chuàng)建出代理對象
    @Adaptive({Constants.PROXY_KEY})
     T getProxy(Invoker invoker) throws RpcException;

    //針對server端,將服務(wù)對象如HelloServiceImpl包裝成一個Invoker對象
    @Adaptive({Constants.PROXY_KEY})
     Invoker getInvoker(T proxy, Class type, URL url) throws RpcException;

}

ProxyFactory的接口實現(xiàn)有JdkProxyFactory、JavassistProxyFactory,默認是JavassistProxyFactory, JavassistProxyFactory內(nèi)容如下:

public class JavassistProxyFactory extends AbstractProxyFactory {

    @SuppressWarnings("unchecked")
    public  T getProxy(Invoker invoker, Class[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    public  Invoker getInvoker(T proxy, Class type, URL url) {
        // TODO Wrapper類不能正確處理帶$的類名
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf("$") < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, 
                                      Class[] parameterTypes, 
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

可以看到創(chuàng)建了一個AbstractProxyInvoker(這類就是本地執(zhí)行的Invoker),AbstractProxyInvoker對invoke()方法的實現(xiàn)如下:

public Result invoke(Invocation invocation) throws RpcException {
    try {
        return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
    } catch (InvocationTargetException e) {
        return new RpcResult(e.getTargetException());
    } catch (Throwable e) {
        throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

綜上所述,服務(wù)發(fā)布的第二個過程就是:使用ProxyFactory將HelloServiceImpl封裝成一個本地執(zhí)行的Invoker。

Protocol

從上面得知服務(wù)發(fā)布的第一、二個過程:

1、獲取注冊中心信息dubbo:registry和協(xié)議信息dubbo:protocol。
2、使用ProxyFactory將HelloServiceImpl封裝成一個本地執(zhí)行的Invoker。

執(zhí)行這個服務(wù)->執(zhí)行這個本地的Invoker->調(diào)用AbstractProxyInvoker.invoke(Invocation invocation)方法,方法的執(zhí)行過程就是通過反射執(zhí)行HelloServiceImpl。

現(xiàn)在的問題是:客戶端如何調(diào)用服務(wù)端的方法(服務(wù)注冊到注冊中心->客戶端向注冊中心訂閱服務(wù)->客戶端調(diào)用服務(wù)端的方法)和上述Invocation參數(shù)的來源問題。

對于Server端來說,上述服務(wù)發(fā)布的第3步中Protocol要解決的問題是:

根據(jù)指定協(xié)議向注冊中心注冊HelloService服務(wù)。

當(dāng)客戶端根據(jù)協(xié)議調(diào)用這個服務(wù)時,將客戶端傳遞過來的Invocation參數(shù)交給上述的Invoker來執(zhí)行。

所以Protocol會加入遠程通信這塊,根據(jù)客戶端的請求來獲取參數(shù)Invocation。

先來看下Protocol接口的定義:

@SPI("dubbo")
public interface Protocol {

    int getDefaultPort();

    //針對server端來說,將本地執(zhí)行類的Invoker通過協(xié)議暴漏給外部。這樣外部就可以通過協(xié)議發(fā)送執(zhí)行參數(shù)Invocation,然后交給本地Invoker來執(zhí)行
    @Adaptive
     Exporter export(Invoker invoker) throws RpcException;

    //這個是針對客戶端的,客戶端從注冊中心獲取服務(wù)器端發(fā)布的服務(wù)信息
    //通過服務(wù)信息得知服務(wù)器端使用的協(xié)議,然后客戶端仍然使用該協(xié)議構(gòu)造一個Invoker。這個Invoker是遠程通信類的Invoker。
    //執(zhí)行時,需要將執(zhí)行信息通過指定協(xié)議發(fā)送給服務(wù)器端,服務(wù)器端接收到參數(shù)Invocation,然后交給服務(wù)器端的本地Invoker來執(zhí)行
    @Adaptive
     Invoker refer(Class type, URL url) throws RpcException;

    void destroy();

}

我們再來詳細看看服務(wù)發(fā)布的第3步(ServiceConfig):

Exporter exporter = protocol.export(invoker);

protocol的來歷是:

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException{
    if (arg0 == null) {
        throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); 
    }
    if (arg0.getUrl() == null) {
        throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
    }
    com.alibaba.dubbo.common.URL url = arg0.getUrl();
    String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
    if(extName == null) {
        throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); 
    }
    com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)com.alibaba.dubbo.common.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
    return extension.export(arg0);
}

public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException{
    if (arg1 == null) { 
        throw new IllegalArgumentException("url == null"); 
    }
    com.alibaba.dubbo.common.URL url = arg1;
    String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
    if(extName == null) {
        throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); 
    }
    com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)com.alibaba.dubbo.common.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
    return extension.refer(arg0, arg1);
}

export(Invoker invoker)的過程即根據(jù)Invoker中url的信息來最終選擇Protocol的實現(xiàn),默認實現(xiàn)是DubboProtocol,然后再對DubboProtocol進行依賴注入,進行wrap包裝(getExtension()方法)。

Protocol的實現(xiàn)情況:

可以看到在返回DubboProtocol之前,經(jīng)過了ProtocolFilterWrapper、ProtocolListenerWrapper、RegistryProtocol的包裝

所謂包裝就是如下內(nèi)容:

package com.alibaba.xxx;

import com.alibaba.dubbo.rpc.Protocol;

public class XxxProtocolWrapper implemenets Protocol {
    Protocol impl;

    public XxxProtocol(Protocol protocol) { impl = protocol; }

    // 接口方法做一個操作后,再調(diào)用extension的方法
    public Exporter export(final Invoker invoker) {
        //... 一些操作
        impl .export(invoker);
        // ... 一些操作
    }

    // ...
}

使用裝飾器模式,類似AOP的功能

下面主要講解RegistryProtocol和DubboProtocol,先暫時忽略ProtocolFilterWrapper、ProtocolListenerWrapper

RegistryProtocol.export() 主要功能是將服務(wù)注冊到注冊中心

DubboProtocol.export() 服務(wù)導(dǎo)出功能:

創(chuàng)建一個DubboExporter,封裝Invoker。

根據(jù)Invoker的url獲取ExchangeServer通信對象(負責(zé)與客戶端的通信模塊)。

現(xiàn)在我們搞清楚我們的目的,通過通信對象獲取客戶端傳來的Invocation參數(shù),然后找到對應(yīng)的DubboExporter(即能夠獲取到本地Invoker)就可以執(zhí)行服務(wù)了。

在DubboProtocol中,每個ExchangeServer通信對象都綁定了一個ExchangeHandler對象,內(nèi)容如下:

private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {

    public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
        if (message instanceof Invocation) {
            Invocation inv = (Invocation) message;
            Invoker invoker = getInvoker(channel, inv);
            RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
            ......
            return invoker.invoke(inv);
        }
        throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
    }
};

可以看到該類才是真正與客戶端通信,在獲取到Invocation參數(shù)后,調(diào)用getInvoker()來獲取本地的Invoker(先從exporterMap中獲取Exporter),就可以調(diào)用服務(wù)了。

而對于通信這塊,接下來會專門來詳細的說明,從reply參數(shù)可知,重點在了解ExchangeChannel。

Exporter

負責(zé)維護invoker的生命周期,包含一個Invoker對象,接口定義如下:

public interface Exporter {

    Invoker getInvoker();

    void unexport();

}
結(jié)束語

以上就是本文簡略地介紹了及服務(wù)發(fā)布過程中的幾個 ProxyFactory、Invoker、Protocol、Exporter 概念

參考:http://dubbo.apache.org/books/dubbo-dev-book/implementation.html
參考:https://blog.csdn.net/qq418517226/article/details/51818769

Contact

作者:鵬磊

出處:http://www.ymq.io/2018/06/13/dubbo_rpc_export

版權(quán)歸作者所有,轉(zhuǎn)載請注明出處

Wechat:關(guān)注公眾號,搜云庫,專注于開發(fā)技術(shù)的研究與知識分享

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/71184.html

相關(guān)文章

  • Dubbo 一篇文章就夠了:從入門到實戰(zhàn)

    摘要:啟動容器,加載,運行服務(wù)提供者。服務(wù)提供者在啟動時,在注冊中心發(fā)布注冊自己提供的服務(wù)。注冊中心返回服務(wù)提供者地址列表給消費者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費者。 一 為什么需要 dubbo 很多時候,其實我們使用這個技術(shù)的時候,可能都是因為項目需要,所以,我們就用了,但是,至于為什么我們需要用到這個技術(shù),可能自身并不是很了解的,但是,其實了解技術(shù)的來由及背景知識,對...

    tomener 評論0 收藏0
  • Dubbo服務(wù)暴露過程

    摘要:根據(jù)的值,進行服務(wù)暴露。如果配置為則不暴露,如果服務(wù)未配置成,則本地暴露如果未配置成,則暴露遠程服務(wù)。提供者向注冊中心訂閱所有注冊服務(wù)當(dāng)注冊中心有此服務(wù)的覆蓋配置注冊進來時,推送消息給提供者,重新暴露服務(wù),這由管理頁面完成。 概覽 dubbo暴露服務(wù)有兩種情況,一種是設(shè)置了延遲暴露(比如delay=5000),另外一種是沒有設(shè)置延遲暴露或者延遲設(shè)置為-1(delay=-1): 設(shè)置了...

    bigdevil_s 評論0 收藏0
  • Dubbo 2.7.1 踩坑記

    摘要:面試題服務(wù)提供者能實現(xiàn)失效踢出是什么原理高頻題服務(wù)宕機的時候,該節(jié)點由于是持久節(jié)點會永遠存在,而且當(dāng)服務(wù)再次重啟的時候會將重新注冊一個新節(jié)點。 Dubbo 2.7 版本增加新特性,新系統(tǒng)開始使用 Dubbo 2.7.1 嘗鮮新功能。使用過程中不慎踩到這個版本的 Bug。 系統(tǒng)架構(gòu) Spring Boot 2.14-Release + Dubbo 2.7.1 現(xiàn)象 Dubbo 服務(wù)者啟動...

    wudengzan 評論0 收藏0
  • 網(wǎng)關(guān)實現(xiàn)灰度發(fā)布

    摘要:就是一種灰度發(fā)布方式,讓一部分用戶繼續(xù)用,一部分用戶開始用,如果用戶對沒有什么反對意見,那么逐步擴大范圍,把所有用戶都遷移到上面來。灰度發(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時候就可以發(fā)現(xiàn)調(diào)整問題,以保證其影響度。 一、背景互聯(lián)網(wǎng)產(chǎn)品開發(fā)有個非常特別的地方,就是不停的升級,升級,再升級。采用敏捷開發(fā)的方式,基本上保持每周或者每兩周一次的發(fā)布頻率,系統(tǒng)升級總是伴隨著各種風(fēng)險,新舊版本兼...

    stormjun 評論0 收藏0

發(fā)表評論

0條評論

lifesimple

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<