国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

dubbo源碼解析(八)遠(yuǎn)程通信——開篇

Faremax / 423人閱讀

摘要:而編碼器是講應(yīng)用程序的數(shù)據(jù)轉(zhuǎn)化為網(wǎng)絡(luò)格式,解碼器則是講網(wǎng)絡(luò)格式轉(zhuǎn)化為應(yīng)用程序,同時(shí)具備這兩種功能的單一組件就叫編解碼器。在中是老的編解碼器接口,而是新的編解碼器接口,并且已經(jīng)用把適配成了。

遠(yuǎn)程通訊——開篇
目標(biāo):介紹之后解讀遠(yuǎn)程通訊模塊的內(nèi)容如何編排、介紹dubbo-remoting-api中的包結(jié)構(gòu)設(shè)計(jì)以及最外層的的源碼解析。
前言

服務(wù)治理框架中可以大致分為服務(wù)通信和服務(wù)管理兩個(gè)部分,前面我先講到有關(guān)注冊中心的內(nèi)容,也就是服務(wù)管理,當(dāng)然dubbo的服務(wù)管理還包括監(jiān)控中心、 telnet 命令,它們起到的是人工的服務(wù)管理作用,這個(gè)后續(xù)再介紹。接下來我要講解的就是跟服務(wù)通信有關(guān)的部分,也就是遠(yuǎn)程通訊模塊。我在《dubbo源碼解析(一)Hello,Dubbo》的"(六)dubbo-remoting——遠(yuǎn)程通信模塊“中提到過一些內(nèi)容。該模塊中提供了多種客戶端和服務(wù)端通信的功能,而在對NIO框架選型上,dubbo交由用戶選擇,它集成了mina、netty、grizzly等各類NIO框架來搭建NIO服務(wù)器和客戶端,并且利用dubbo的SPI擴(kuò)展機(jī)制可以讓用戶自定義選擇。如果對SPI不太了解的朋友可以查看《dubbo源碼解析(二)Dubbo擴(kuò)展機(jī)制SPI》。

接下來我們先來看看dubbo-remoting的包結(jié)構(gòu):

我接下來解讀遠(yuǎn)程通訊模塊的內(nèi)容并不是按照一個(gè)包一篇文章的編排,先來看看dubbo-remoting-api的包結(jié)構(gòu):

可以看到,大篇幅的邏輯在dubbo-remoting-api中,所以我對于dubbo-remoting-api的解讀會(huì)分為下面五個(gè)部分來說明,其中第五點(diǎn)會(huì)在本文介紹,其他四點(diǎn)會(huì)分別用四篇文章來介紹:

buffer包:緩沖在NIO框架中是很重要的存在,各個(gè)NIO框架都實(shí)現(xiàn)了自己相應(yīng)的緩存操作。這個(gè)buffer包下包括了緩沖區(qū)的接口以及抽象

exchange包:信息交換層,其中封裝了請求響應(yīng)模式,在傳輸層之上重新封裝了 Request-Response 語義,為了滿足RPC的需求。這層可以認(rèn)為專注在Request和Response攜帶的信息上。該層是RPC調(diào)用的通訊基礎(chǔ)之一。

telnet包:dubbo支持通過telnet命令來進(jìn)行服務(wù)治理,該包下就封裝了這些通用指令的邏輯實(shí)現(xiàn)。

transport包:網(wǎng)絡(luò)傳輸層,它只負(fù)責(zé)單向消息傳輸,是對 Mina, Netty, Grizzly 的抽象,它也可以擴(kuò)展 UDP 傳輸。該層是RPC調(diào)用的通訊基礎(chǔ)之一。

最外層的源碼:該部分我會(huì)在下面之間給出介紹。

為什么我要把一個(gè)api分成這么多文章來講解,我們先來看看下面的圖:

我們可以看到紅框內(nèi)的是遠(yuǎn)程通訊的框架,序列化我會(huì)在后面的主題中介紹,而Exchange層和Transport層在框架設(shè)計(jì)中起到了很重要的作用,也是支撐Remoting的核心,所以我要分開來介紹。

除了上述的五點(diǎn)外,根據(jù)慣例,我還是會(huì)分別介紹dubbo支持的實(shí)現(xiàn)客戶端和服務(wù)端通信的七種方案,也就是說該遠(yuǎn)程通訊模塊我會(huì)用12篇文章詳細(xì)的講解。

最外層源碼解析 (一)接口Endpoint

dubbo抽象出一個(gè)端的概念,也就是Endpoint接口,這個(gè)端就是一個(gè)點(diǎn),而點(diǎn)對點(diǎn)之間是可以雙向傳輸。在端的基礎(chǔ)上在衍生出通道、客戶端以及服務(wù)端的概念,也就是下面要介紹的Channel、Client、Server三個(gè)接口。在傳輸層,其實(shí)Client和Server的區(qū)別只是在語義上區(qū)別,并不區(qū)分請求和應(yīng)答職責(zé),在交換層客戶端和服務(wù)端也是一個(gè)點(diǎn),但是已經(jīng)是有方向的點(diǎn),所以區(qū)分了明確的請求和應(yīng)答職責(zé)。兩者都具備發(fā)送的能力,只是客戶端和服務(wù)端所關(guān)注的事情不一樣,這個(gè)在后面會(huì)分開介紹,而Endpoint接口抽象的方法就是它們共同擁有的方法。這也就是它們都能被抽象成端的原因。

來看一下它的源碼:

public interface Endpoint {

    // 獲得該端的url
    URL getUrl();

    // 獲得該端的通道處理器
    ChannelHandler getChannelHandler();
    
    // 獲得該端的本地地址
    InetSocketAddress getLocalAddress();
    
    // 發(fā)送消息
    void send(Object message) throws RemotingException;
    
    // 發(fā)送消息,sent是是否已經(jīng)發(fā)送的標(biāo)記
    void send(Object message, boolean sent) throws RemotingException;
    
    // 關(guān)閉
    void close();
    
    // 優(yōu)雅的關(guān)閉,也就是加入了等待時(shí)間
    void close(int timeout);
    
    // 開始關(guān)閉
    void startClose();
    
    // 判斷是否已經(jīng)關(guān)閉
    boolean isClosed();

}

前三個(gè)方法是獲得該端本身的一些屬性,

兩個(gè)send方法是發(fā)送消息,其中第二個(gè)方法多了一個(gè)sent的參數(shù),為了區(qū)分是否是第一次發(fā)送消息。

后面幾個(gè)方法是提供了關(guān)閉通道的操作以及判斷通道是否關(guān)閉的操作。

(二)接口Channel

該接口是通道接口,通道是通訊的載體。還是用自動(dòng)販賣機(jī)的例子,自動(dòng)販賣機(jī)就好比是一個(gè)通道,消息發(fā)送端會(huì)往通道輸入消息,而接收端會(huì)從通道讀消息。并且接收端發(fā)現(xiàn)通道沒有消息,就去做其他事情了,不會(huì)造成阻塞。所以channel可以讀也可以寫,并且可以異步讀寫。channel是client和server的傳輸橋梁。channel和client是一一對應(yīng)的,也就是一個(gè)client對應(yīng)一個(gè)channel,但是channel和server是多對一對關(guān)系,也就是一個(gè)server可以對應(yīng)多個(gè)channel。

public interface Channel extends Endpoint {

    // 獲得遠(yuǎn)程地址
    InetSocketAddress getRemoteAddress();

    // 判斷通道是否連接
    boolean isConnected();

    // 判斷是否有該key的值
    boolean hasAttribute(String key);

    // 獲得該key對應(yīng)的值
    Object getAttribute(String key);

    // 添加屬性
    void setAttribute(String key, Object value);

    // 移除屬性
    void removeAttribute(String key);

}

可以看到Channel繼承了Endpoint,也就是端抽象出來的方法也同樣是channel所需要的。上面的幾個(gè)方法很好理解,我就不多介紹了。

(三)接口ChannelHandler
@SPI
public interface ChannelHandler {

    // 連接該通道
    void connected(Channel channel) throws RemotingException;

    // 斷開該通道
    void disconnected(Channel channel) throws RemotingException;

    // 發(fā)送給這個(gè)通道消息
    void sent(Channel channel, Object message) throws RemotingException;

    // 從這個(gè)通道內(nèi)接收消息
    void received(Channel channel, Object message) throws RemotingException;

    // 從這個(gè)通道內(nèi)捕獲異常
    void caught(Channel channel, Throwable exception) throws RemotingException;

}

該接口是負(fù)責(zé)channel中的邏輯處理,并且可以看到這個(gè)接口有注解@SPI,是個(gè)可擴(kuò)展接口,到時(shí)候都會(huì)在下面介紹各類NIO框架的時(shí)候會(huì)具體講到它的實(shí)現(xiàn)類。

(四)接口Client
public interface Client extends Endpoint, Channel, Resetable {
    
    // 重連
    void reconnect() throws RemotingException;

    // 重置,不推薦使用
    @Deprecated
    void reset(com.alibaba.dubbo.common.Parameters parameters);

}

客戶端接口,可以看到它繼承了Endpoint、Channel和Resetable接口,繼承Endpoint的原因上面我已經(jīng)提到過了,客戶端和服務(wù)端其實(shí)只是語義上的不同,客戶端就是一個(gè)點(diǎn)。繼承Channel是因?yàn)榭蛻舳烁ǖ朗且灰粚?yīng)的,所以做了這樣的設(shè)計(jì),還繼承了Resetable接口是為了實(shí)現(xiàn)reset方法,該方法,不過已經(jīng)打上@Deprecated注解,不推薦使用。除了這些客戶端就只需要關(guān)注一個(gè)重連的操作。

這里插播一個(gè)公共模塊下的接口Resetable:

public interface Resetable {

    // 用于根據(jù)新傳入的 url 屬性,重置自己內(nèi)部的一些屬性
    void reset(URL url);

}

該方法就是根據(jù)新的url來重置內(nèi)部的屬性。

(五)接口Server
public interface Server extends Endpoint, Resetable {

    // 判斷是否綁定到本地端口,也就是該服務(wù)器是否啟動(dòng)成功,能夠連接、接收消息,提供服務(wù)。
    boolean isBound();

    // 獲得連接該服務(wù)器的通道
    Collection getChannels();

    // 通過遠(yuǎn)程地址獲得該地址對應(yīng)的通道
    Channel getChannel(InetSocketAddress remoteAddress);

    @Deprecated
    void reset(com.alibaba.dubbo.common.Parameters parameters);

}

該接口是服務(wù)端接口,繼承了Endpoint和Resetable,繼承Endpoint是因?yàn)榉?wù)端也是一個(gè)點(diǎn),繼承Resetable接口是為了繼承reset方法。除了這些以外,服務(wù)端獨(dú)有的是檢測是否啟動(dòng)成功,還有事獲得連接該服務(wù)器上所有通道,這里獲得所有通道其實(shí)就意味著獲得了所有連接該服務(wù)器的客戶端,因?yàn)榭蛻舳撕屯ǖ朗且灰粚?yīng)的。

(六)接口Codec && Codec2

這兩個(gè)都是編解碼器,那么什么叫做編解碼器,在網(wǎng)絡(luò)中只是講數(shù)據(jù)看成是原始的字節(jié)序列,但是我們的應(yīng)用程序會(huì)把這些字節(jié)組織成有意義的信息,那么網(wǎng)絡(luò)字節(jié)流和數(shù)據(jù)間的轉(zhuǎn)化就是很常見的任務(wù)。而編碼器是講應(yīng)用程序的數(shù)據(jù)轉(zhuǎn)化為網(wǎng)絡(luò)格式,解碼器則是講網(wǎng)絡(luò)格式轉(zhuǎn)化為應(yīng)用程序,同時(shí)具備這兩種功能的單一組件就叫編解碼器。在dubbo中Codec是老的編解碼器接口,而Codec2是新的編解碼器接口,并且dubbo已經(jīng)用CodecAdapter把Codec適配成Codec2了。所以在這里我就介紹Codec2接口,畢竟人總要往前看。

@SPI
public interface Codec2 {
      //編碼
    @Adaptive({Constants.CODEC_KEY})
    void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException;
    //解碼
    @Adaptive({Constants.CODEC_KEY})
    Object decode(Channel channel, ChannelBuffer buffer) throws IOException;

    enum DecodeResult {
        // 需要更多輸入和忽略一些輸入
        NEED_MORE_INPUT, SKIP_SOME_INPUT
    }

}

因?yàn)槭蔷幗獯a器,所以有兩個(gè)方法分別是編碼和解碼,上述有以下幾個(gè)關(guān)注的:

Codec2是一個(gè)可擴(kuò)展的接口,因?yàn)橛蠤SPI注解。

用到了Adaptive機(jī)制,首先去url中尋找key為codec的value,來加載url攜帶的配置中指定的codec的實(shí)現(xiàn)。

該接口中有個(gè)枚舉類型DecodeResult,因?yàn)榻獯a過程中,需要解決 TCP 拆包、粘包的場景,所以增加了這兩種解碼結(jié)果,關(guān)于TCP 拆包、粘包的場景我就不多解釋,不懂得朋友可以google一下。

(七)接口Decodeable
public interface Decodeable {

    //解碼
    public void decode() throws Exception;

}

該接口是可解碼的接口,該接口有兩個(gè)作用,第一個(gè)是在調(diào)用真正的decode方法實(shí)現(xiàn)的時(shí)候會(huì)有一些校驗(yàn),判斷是否可以解碼,并且對解碼失敗會(huì)有一些消息設(shè)置;第二個(gè)是被用來message核對用的。后面看具體的實(shí)現(xiàn)會(huì)更了解該接口的作用。

(八)接口Dispatcher
@SPI(AllDispatcher.NAME)
public interface Dispatcher {

    // 調(diào)度
    @Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
    // The last two parameters are reserved for compatibility with the old configuration
    ChannelHandler dispatch(ChannelHandler handler, URL url);

}

該接口是調(diào)度器接口,dispatch是線程池的調(diào)度方法,這邊有幾個(gè)注意點(diǎn):

該接口是一個(gè)可擴(kuò)展接口,并且默認(rèn)實(shí)現(xiàn)AllDispatcher,也就是所有消息都派發(fā)到線程池,包括請求,響應(yīng),連接事件,斷開事件,心跳等。

用了Adaptive注解,也就是按照URL中配置來加載實(shí)現(xiàn)類,后面兩個(gè)參數(shù)是為了兼容老版本,如果這是三個(gè)key對應(yīng)的值都為空,就選擇AllDispatcher來實(shí)現(xiàn)。

(九)接口Transporter
@SPI("netty")
public interface Transporter {
    
    // 綁定一個(gè)服務(wù)器
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;
    
    // 連接一個(gè)服務(wù)器,即創(chuàng)建一個(gè)客戶端
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;

}

該接口是網(wǎng)絡(luò)傳輸接口,有以下幾個(gè)注意點(diǎn):

該接口是一個(gè)可擴(kuò)展的接口,并且默認(rèn)實(shí)現(xiàn)NettyTransporter。

用了dubbo SPI擴(kuò)展機(jī)制中的Adaptive注解,加載對應(yīng)的bind方法,使用url攜帶的server或者transporter屬性值,加載對應(yīng)的connect方法,使用url攜帶的client或者transporter屬性值,不了解SPI擴(kuò)展機(jī)制的可以查看《dubbo源碼解析(二)Dubbo擴(kuò)展機(jī)制SPI》。

(十)Transporters
public class Transporters {

    static {
        // check duplicate jar package
        // 檢查重復(fù)的 jar 包
        Version.checkDuplicate(Transporters.class);
        Version.checkDuplicate(RemotingException.class);
    }

    private Transporters() {
    }

    public static Server bind(String url, ChannelHandler... handler) throws RemotingException {
        return bind(URL.valueOf(url), handler);
    }

    public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        if (handlers == null || handlers.length == 0) {
            throw new IllegalArgumentException("handlers == null");
        }
        ChannelHandler handler;
        // 創(chuàng)建handler
        if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }
        // 調(diào)用Transporter的實(shí)現(xiàn)類對象的bind方法。
        // 例如實(shí)現(xiàn)NettyTransporter,則調(diào)用NettyTransporter的connect,并且返回相應(yīng)的server
        return getTransporter().bind(url, handler);
    }

    public static Client connect(String url, ChannelHandler... handler) throws RemotingException {
        return connect(URL.valueOf(url), handler);
    }

    public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        ChannelHandler handler;
        if (handlers == null || handlers.length == 0) {
            handler = new ChannelHandlerAdapter();
        } else if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }
        // 調(diào)用Transporter的實(shí)現(xiàn)類對象的connect方法。
        // 例如實(shí)現(xiàn)NettyTransporter,則調(diào)用NettyTransporter的connect,并且返回相應(yīng)的client
        return getTransporter().connect(url, handler);
    }

    public static Transporter getTransporter() {
        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }

}

該類用到了設(shè)計(jì)模式的外觀模式,通過該類的包裝,我們就不會(huì)看到內(nèi)部具體的實(shí)現(xiàn)細(xì)節(jié),這樣降低了程序的復(fù)雜度,也提高了程序的可維護(hù)性。比如這個(gè)類,包裝了調(diào)用各種實(shí)現(xiàn)Transporter接口的方法,通過getTransporter來獲得Transporter的實(shí)現(xiàn)對象,具體實(shí)現(xiàn)哪個(gè)實(shí)現(xiàn)類,取決于url中攜帶的配置信息,如果url中沒有相應(yīng)的配置,則默認(rèn)選擇@SPI中的默認(rèn)值netty。

bind和connect方法分別有兩個(gè)重載方法,其中的操作只是把把字符串的url轉(zhuǎn)化為URL對象。

靜態(tài)代碼塊中檢測了一下jar包是否有重復(fù)。

(十一)RemotingException && ExecutionException && TimeoutException

這三個(gè)類是遠(yuǎn)程通信的異常類:

RemotingException繼承了Exception類,是遠(yuǎn)程通信的基礎(chǔ)異常。

ExecutionException繼承了RemotingException類,ExecutionException是遠(yuǎn)程通信的執(zhí)行異常。

TimeoutException繼承了RemotingException類,TimeoutException是超時(shí)異常。

為了不影響篇幅,這三個(gè)類源碼我就不介紹了,因?yàn)楸容^簡單。

后記
該部分相關(guān)的源碼解析地址:https://github.com/CrazyHZM/i...

該文章講解了dubbo-remoting-api中的包結(jié)構(gòu)設(shè)計(jì)以及最外層的的源碼解析,其中關(guān)鍵的是理解端的概念,明白在哪一層才區(qū)分了發(fā)送和接收的職責(zé),后續(xù)文章會(huì)按照我上面的編排去寫。如果我在哪一部分寫的不夠到位或者寫錯(cuò)了,歡迎給我提意見,我的私人微信號(hào)碼:HUA799695226。

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/72579.html

相關(guān)文章

  • dubbo源碼解析(十九)遠(yuǎn)程調(diào)用——開篇

    摘要:遠(yuǎn)程調(diào)用開篇目標(biāo)介紹之后解讀遠(yuǎn)程調(diào)用模塊的內(nèi)容如何編排介紹中的包結(jié)構(gòu)設(shè)計(jì)以及最外層的的源碼解析。十該類就是遠(yuǎn)程調(diào)用的上下文,貫穿著整個(gè)調(diào)用,例如調(diào)用,然后調(diào)用。十五該類是系統(tǒng)上下文,僅供內(nèi)部使用。 遠(yuǎn)程調(diào)用——開篇 目標(biāo):介紹之后解讀遠(yuǎn)程調(diào)用模塊的內(nèi)容如何編排、介紹dubbo-rpc-api中的包結(jié)構(gòu)設(shè)計(jì)以及最外層的的源碼解析。 前言 最近我面臨著一個(gè)選擇,因?yàn)閐ubbo 2.7.0-...

    jayce 評論0 收藏0
  • dubbo源碼解析(四十七)服務(wù)端處理請求過程

    摘要:而存在的意義就是保證請求或響應(yīng)對象可在線程池中被解碼,解碼完成后,就會(huì)分發(fā)到的。 2.7大揭秘——服務(wù)端處理請求過程 目標(biāo):從源碼的角度分析服務(wù)端接收到請求后的一系列操作,最終把客戶端需要的值返回。 前言 上一篇講到了消費(fèi)端發(fā)送請求的過程,該篇就要將服務(wù)端處理請求的過程。也就是當(dāng)服務(wù)端收到請求數(shù)據(jù)包后的一系列處理以及如何返回最終結(jié)果。我們也知道消費(fèi)端在發(fā)送請求的時(shí)候已經(jīng)做了編碼,所以我...

    yzzz 評論0 收藏0
  • dubbo源碼解析(四十六)消費(fèi)端發(fā)送請求過程

    摘要:可以參考源碼解析二十四遠(yuǎn)程調(diào)用協(xié)議的八。十六的該類也是用了適配器模式,該類主要的作用就是增加了心跳功能,可以參考源碼解析十遠(yuǎn)程通信層的四。二十的可以參考源碼解析十七遠(yuǎn)程通信的一。 2.7大揭秘——消費(fèi)端發(fā)送請求過程 目標(biāo):從源碼的角度分析一個(gè)服務(wù)方法調(diào)用經(jīng)歷怎么樣的磨難以后到達(dá)服務(wù)端。 前言 前一篇文章講到的是引用服務(wù)的過程,引用服務(wù)無非就是創(chuàng)建出一個(gè)代理。供消費(fèi)者調(diào)用服務(wù)的相關(guān)方法。...

    fish 評論0 收藏0
  • dubbo源碼解析(四十)異步化改造

    摘要:大揭秘異步化改造目標(biāo)從源碼的角度分析的新特性中對于異步化的改造原理。看源碼解析四十六消費(fèi)端發(fā)送請求過程講到的十四的,在以前的邏輯會(huì)直接在方法中根據(jù)配置區(qū)分同步異步單向調(diào)用。改為關(guān)于可以參考源碼解析十遠(yuǎn)程通信層的六。 2.7大揭秘——異步化改造 目標(biāo):從源碼的角度分析2.7的新特性中對于異步化的改造原理。 前言 dubbo中提供了很多類型的協(xié)議,關(guān)于協(xié)議的系列可以查看下面的文章: du...

    lijinke666 評論0 收藏0
  • dubbo源碼解析(四十五)服務(wù)引用過程

    摘要:服務(wù)引用過程目標(biāo)從源碼的角度分析服務(wù)引用過程。并保留服務(wù)提供者的部分配置,比如版本,,時(shí)間戳等最后將合并后的配置設(shè)置為查詢字符串中。的可以參考源碼解析二十三遠(yuǎn)程調(diào)用的一的源碼分析。 dubbo服務(wù)引用過程 目標(biāo):從源碼的角度分析服務(wù)引用過程。 前言 前面服務(wù)暴露過程的文章講解到,服務(wù)引用有兩種方式,一種就是直連,也就是直接指定服務(wù)的地址來進(jìn)行引用,這種方式更多的時(shí)候被用來做服務(wù)測試,不...

    xiaowugui666 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<