Dubbo远程通信模块简析

   日期:2020-05-26     浏览:100    评论:0    
核心提示:dubbo-remoting 模块整体结构设计服务治理框架 大致可分为 “服务通信” 和 “服务管理” 两部分,前面我们分析了有关注册中心的源码,也就是服务管理,接下来要分析的就是跟服务通信有关的源码,也就是远程通讯模块。该模块中提供了多种客户端和服务端通信的功能,而在对NIO框架选型上,dubbo交由用户选择,它集成了mina、netty、grizzly等各类NIO框架来搭建NIO服务器和客户端,并且利用dubbo的SPI扩展机制可以让用户自定义选择。dubbo-remoting的工程结构如下。duja

dubbo-remoting 模块整体结构设计

服务治理框架 大致可分为 “服务通信” 和 “服务管理” 两部分,前面我们分析了有关注册中心的源码,也就是服务管理,接下来要分析的就是跟服务通信有关的源码,也就是远程通讯模块。该模块中提供了多种客户端和服务端通信的功能,而在对NIO框架选型上,dubbo交由用户选择,它集成了mina、netty、grizzly等各类NIO框架来搭建NIO服务器和客户端,并且利用dubbo的SPI扩展机制可以让用户自定义选择。dubbo-remoting的工程结构如下。

dubbo-remoting-api 模块整体结构设计

本篇我们先来看一下 dubbo-remoting 中 dubbo-remoting-api的项目结构。

dubbo-remoting-api 定义了远程通信模块最核心的 API,对于 dubbo-remoting-api 的解读会分为如下五个部分,其中第五部分会在本文介绍。

  1. buffer包:缓冲在 NIO框架 中是很重要的存在,各个 NIO框架 都实现了自己相应的缓存操作。这个 buffer包 下包括了缓冲区的接口以及抽象类;
  2. exchange包:信息交换层,其中封装了请求响应模式,在传输层之上重新封装了 Request-Response 语义,为了满足 RPC 的需求。这层可以认为专注在 Request 和 Response 携带的信息上。该层是 RPC调用 的通讯基础之一;
  3. telnet包:dubbo 支持通过 telnet命令 来进行服务治理,该包下就封装了这些通用指令的逻辑实现;
  4. transport包:网络传输层,它只负责单向消息传输,是对 Mina、Netty、Grizzly 的抽象,它也可以扩展 UDP 传输,该层也是 RPC调用 的通讯基础之一;
  5. 最外层的源码:该部分也是我们接下来要重点解析的。

结合 dubbo-remoting-api模块 的外层类和包划分,我们看看下面的官方架构图。

红框标注的部分是 dubbo整体架构中的 远程通讯架构,其中 Exchange组件 和 Transport组件 在框架设计中起到了很重要的作用,也是支撑 Remoting 的核心。

dubbo-remoting-api 模块最外层源码解析

Endpoint 接口

dubbo 抽象出了一个端的概念,也就是Endpoint接口,这个端就是一个点,而点与点之间可以双向传输。在端的基础上再衍生出通道、客户端以及服务端的概念,也就是下面要介绍的 Channel、Client、Server 三个接口。在传输层,Client 和 Server 的区别只是语义上的区别,并不区分请求和应答职责,而在交换层,Client 和 Server 是有方向的端点,所以区分了明确的请求和应答职责。两者都具备发送的能力,只是客户端和服务端所关注的事情不一样,而Endpoint接口抽象的方法就是它们共同拥有的方法。这也就是它们都能被抽象成端的原因。


public interface Endpoint {

    
    URL getUrl();

    
    ChannelHandler getChannelHandler();

    
    InetSocketAddress getLocalAddress();

    
    void send(Object message) throws RemotingException;

    
    void send(Object message, boolean sent) throws RemotingException;

    
    void close();

    
    void close(int timeout);

    void startClose();

    
    boolean isClosed();
}

Channel 接口

该接口是通道接口,通道是信息传输的载体。Channel 可读可写,并且可以异步读写。Channel 是 client 和 server 的数据传输桥梁。Channel 和 client 是一对一的,也就是一个 client 对应一个 Channel,而 Channel 和 server 则是多对一,也就是一个 server 可以对应多个 Channel。


public interface Channel extends Endpoint {

	
    InetSocketAddress getRemoteAddress();

    
    boolean isConnected();

    
    boolean hasAttribute(String key);

    
    Object getAttribute(String key);

    
    void setAttribute(String key, Object value);

    
    void removeAttribute(String key);
}

ChannelHandler 接口


@SPI
public interface ChannelHandler {

    
    void connected(Channel channel) throws RemotingException;

    
    void disconnected(Channel channel) throws RemotingException;

    
    void sent(Channel channel, Object message) throws RemotingException;

    
    void received(Channel channel, Object message) throws RemotingException;

    
    void caught(Channel channel, Throwable exception) throws RemotingException;

}

Client 和 Resetable 接口


public interface Client extends Endpoint, Channel, Resetable {

    
    void reconnect() throws RemotingException;

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

public interface Resetable {

    // 用于根据新传入的 url 属性,重置自己内部的一些属性
    void reset(URL url);

}

Server 接口


public interface Server extends Endpoint, Resetable {

    
    boolean isBound();

    
    Collection<Channel> getChannels();

    
    Channel getChannel(InetSocketAddress remoteAddress);

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

Codec2 接口

这两个都是编解码器 接口,在网络中进行传输的数据 都是原始的字节序列,这就需要 发送端使用编码器把 要传输的有意义的信息 序列化成字节序列,接收端再使用解码器 把字节序列再反序列化成 有效信息,而同时具备这两种功能的单一组件就叫 编解码器。在 dubbo 中 Codec 是老编解码器接口,而Codec2是新编解码器接口,并且 dubbo 已经用 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
    }
}

Decodeable 接口


public interface Decodeable {

    
    void decode() throws Exception;
}

Dispatcher 接口


@SPI(AllDispatcher.NAME)
public interface Dispatcher {

    
    @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);
}

Transporter 接口


@SPI("netty")
public interface Transporter {

    
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;

    
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

Transporters 类


public class Transporters {

    static {
        // 检查重复的 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");
        }
        // 创建 handler
        ChannelHandler handler;
        if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }
        // 调用Transporter的实现类对象的bind方法。
        // 例如实现NettyTransporter,则调用NettyTransporter的connect,并且返回相应的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");
        }
        // 创建 handler
        ChannelHandler handler;
        if (handlers == null || handlers.length == 0) {
            handler = new ChannelHandlerAdapter();
        } else if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }
        // 调用Transporter的实现类对象的connect方法。
        // 例如实现NettyTransporter,则调用NettyTransporter的connect,并且返回相应的client
        return getTransporter().connect(url, handler);
    }

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

远程通信的异常类

RemotingException、ExecutionException 和 TimeoutException 是远程通信的异常类,内容比较简单,这里就简单介绍下 一笔带过咯。

  1. RemotingException 继承了 Exception类,是远程通信的基础异常;
  2. ExecutionException 继承了 RemotingException类,ExecutionException 是远程通信的执行异常;
  3. TimeoutException 继承了 RemotingException类,TimeoutException是超时异常。
 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
更多>相关资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服