• Dubbo源码学习总结系列二 dubborpc远程调用模块


            dubbo本质是一个RPC框架,我们首先讨论这个骨干中的骨干,dubbo-rpc模块。

            主要讨论一下几部分内容:

            一、此模块在dubbo整体框架中的作用;

            二、此模块需要完成的需求功能点及接口定义;

            三、DubboProtocol实现细节;

            四、其他协议实现;

            一、此模块在dubbo整体框架中的作用

            Protocol 是框架中的核心层,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用,然后在 Invoker 的主过程上 Filter 拦截点。它抽象了动态代理,只包含一对一的调用,不关心集群的管理。

            二、此模块需要完成的需求功能点及接口定义

            远程调用层:封装 RPC 调用,以 InvocationResult 为中心,扩展接口为 Protocol,InvokerExporter。        

            远程调用模块实现Protocol,Invoker, Exporter等上层协议接口定义,实现DubboProtocol协议的上层实现,以及DubboCodec类(dubbo编码)实现;封装了Hession协议、RMI协议、Http协议、WebService协议、Rest协议、Thrift等协议的实现;抽象了动态代理,只包含一对一的调用,不关心集群的管理。

            核心接口定义有:

            协议接口:Protocol

     1 package com.alibaba.dubbo.rpc;
     2 
     3 import com.alibaba.dubbo.common.URL;
     4 import com.alibaba.dubbo.common.extension.Adaptive;
     5 import com.alibaba.dubbo.common.extension.SPI;
     6 
     7 /**
     8  * Protocol. (API/SPI, Singleton, ThreadSafe)
     9  */
    10 @SPI("dubbo")
    11 public interface Protocol {
    12 
    13     /**
    14      * Get default port when user doesn't config the port.
    15      *
    16      * @return default port
    17      */
    18     int getDefaultPort();
    19 
    20     /**
    21      * Export service for remote invocation: <br>
    22      * 1. Protocol should record request source address after receive a request:
    23      * RpcContext.getContext().setRemoteAddress();<br>
    24      * 2. export() must be idempotent, that is, there's no difference between invoking once and invoking twice when
    25      * export the same URL<br>
    26      * 3. Invoker instance is passed in by the framework, protocol needs not to care <br>
    27      *
    28      * @param <T>     Service type
    29      * @param invoker Service invoker
    30      * @return exporter reference for exported service, useful for unexport the service later
    31      * @throws RpcException thrown when error occurs during export the service, for example: port is occupied
    32      */
    33     @Adaptive
    34     <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    35 
    36     /**
    37      * Refer a remote service: <br>
    38      * 1. When user calls `invoke()` method of `Invoker` object which's returned from `refer()` call, the protocol
    39      * needs to correspondingly execute `invoke()` method of `Invoker` object <br>
    40      * 2. It's protocol's responsibility to implement `Invoker` which's returned from `refer()`. Generally speaking,
    41      * protocol sends remote request in the `Invoker` implementation. <br>
    42      * 3. When there's check=false set in URL, the implementation must not throw exception but try to recover when
    43      * connection fails.
    44      *
    45      * @param <T>  Service type
    46      * @param type Service class
    47      * @param url  URL address for the remote service
    48      * @return invoker service's local proxy
    49      * @throws RpcException when there's any error while connecting to the service provider
    50      */
    51     @Adaptive
    52     <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    53 
    54     /**
    55      * Destroy protocol: <br>
    56      * 1. Cancel all services this protocol exports and refers <br>
    57      * 2. Release all occupied resources, for example: connection, port, etc. <br>
    58      * 3. Protocol can continue to export and refer new service even after it's destroyed.
    59      */
    60     void destroy();
    61 
    62 }

            调用者接口Invoker:

     1 package com.alibaba.dubbo.rpc;
     2 
     3 import com.alibaba.dubbo.common.Node;
     4 
     5 /**
     6  * Invoker. (API/SPI, Prototype, ThreadSafe)
     7  *
     8  * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, com.alibaba.dubbo.common.URL)
     9  * @see com.alibaba.dubbo.rpc.InvokerListener
    10  * @see com.alibaba.dubbo.rpc.protocol.AbstractInvoker
    11  */
    12 public interface Invoker<T> extends Node {
    13 
    14     /**
    15      * get service interface.
    16      *
    17      * @return service interface.
    18      */
    19     Class<T> getInterface();
    20 
    21     /**
    22      * invoke.
    23      *
    24      * @param invocation
    25      * @return result
    26      * @throws RpcException
    27      */
    28     Result invoke(Invocation invocation) throws RpcException;
    29 
    30 }

            发布者接口Exporter:

    package com.alibaba.dubbo.rpc;
    
    /**
     * Exporter. (API/SPI, Prototype, ThreadSafe)
     *
     * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
     * @see com.alibaba.dubbo.rpc.ExporterListener
     * @see com.alibaba.dubbo.rpc.protocol.AbstractExporter
     */
    public interface Exporter<T> {
    
        /**
         * get invoker.
         *
         * @return invoker
         */
        Invoker<T> getInvoker();
    
        /**
         * unexport.
         * <p>
         * <code>
         * getInvoker().destroy();
         * </code>
         */
        void unexport();
    
    }

            请求调用上下文接口Invocation:

    package com.alibaba.dubbo.rpc;
    
    import java.util.Map;
    
    /**
     * Invocation. (API, Prototype, NonThreadSafe)
     *
     * @serial Don't change the class name and package name.
     * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
     * @see com.alibaba.dubbo.rpc.RpcInvocation
     */
    public interface Invocation {
    
        /**
         * get method name.
         *
         * @return method name.
         * @serial
         */
        String getMethodName();
    
        /**
         * get parameter types.
         *
         * @return parameter types.
         * @serial
         */
        Class<?>[] getParameterTypes();
    
        /**
         * get arguments.
         *
         * @return arguments.
         * @serial
         */
        Object[] getArguments();
    
        /**
         * get attachments.
         *
         * @return attachments.
         * @serial
         */
        Map<String, String> getAttachments();
    
        /**
         * get attachment by key.
         *
         * @return attachment value.
         * @serial
         */
        String getAttachment(String key);
    
        /**
         * get attachment by key with default value.
         *
         * @return attachment value.
         * @serial
         */
        String getAttachment(String key, String defaultValue);
    
        /**
         * get the invoker in current context.
         *
         * @return invoker.
         * @transient
         */
        Invoker<?> getInvoker();
    
    }

           

            dubbo-rpc-api模块定义了基本接口,给出了默认实现类:

            Protocol接口的默认实现为AbstractProtocol,AbstractProxyProtocol类

            (1)AbstractProtocol实现了destory()方法,主要逻辑为:循环销毁invoker、exporter。

            (2)AbstractProxyProtocol给出了export()和refer()的默认实现,分别返回AbstractExport和AbstractInvoker实例。

            三、DubboProtocol实现细节(dubbo-rpc-default);

            Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。

            反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。

            特性

            缺省协议,使用基于 mina 1.1.7 和 hessian 3.2.1 的 tbremoting 交互。

    • 连接个数:单连接
    • 连接方式:长连接
    • 传输协议:TCP
    • 传输方式:NIO 异步传输(默认netty)
    • 序列化:Hessian2 二进制序列化
    • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
    • 适用场景:常规远程服务方法调用

            Dubbo协议详细说明参加dubbo用户手册

            核心类:DubboProtocol

            1、实现了export()方法,此方法实现了服务提供者接口的对外发布,返回Exporter对象,用于得到invoker并调用,以及销毁exporter对象。

            实现逻辑为:

            (1)判断是否需要创建stub支持,需要则在stubServiceMethodsMap缓存中存储dubbo.stub.event.methods方法名;

        Stub本地存根:远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub 1,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。

            (2)创建或更新(reset)远程服务,并放到serverMap缓存中;创建远程服务逻辑为:给传入的URL设置默认属性,包括:当服务通道关闭时发送只读事件(channel.readonly.sent),开启心跳检测(维护长连接),设置默认编码方式为dubbo,创建绑定url和请求处理类requestHandler的ExchangeServer并返回;

      1 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
      2         URL url = invoker.getUrl();
      3 
      4         // export service.
      5         String key = serviceKey(url);
      6         DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
      7         exporterMap.put(key, exporter);
      8 
      9         //发布一个本地存根服务,用于分发事件(猜测,不确定)
     10         Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
     11         Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
     12         if (isStubSupportEvent && !isCallbackservice) {
     13             String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
     14             if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
     15                 if (logger.isWarnEnabled()) {
     16                     logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
     17                             "], has set stubproxy support event ,but no stub methods founded."));
     18                 }
     19             } else {
     20                 stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
     21             }
     22         }
     23 
     24         openServer(url);
     25         optimizeSerialization(url);
     26         return exporter;
     27     }
     28 
     29     private void openServer(URL url) {
     30         // find server.
     31         String key = url.getAddress();
     32         //client can export a service which's only for server to invoke
     33         boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
     34         if (isServer) {
     35             ExchangeServer server = serverMap.get(key);
     36             if (server == null) {
     37                 serverMap.put(key, createServer(url));
     38             } else {
     39                 // server supports reset, use together with override
     40                 server.reset(url);
     41             }
     42         }
     43     }
     44 
     45     private ExchangeServer createServer(URL url) {
     46         // 当服务通道关闭时,发送只读事件,默认为True
     47         url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
     48         // 默认开启心跳检测
     49         url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
     50         //约定传输服务为netty实现
     51         String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
     52 
     53         if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
     54             throw new RpcException("Unsupported server type: " + str + ", url: " + url);
     55         //默认使用dubbo序列化
     56         url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
     57         ExchangeServer server;
     58         try {
     59             //创建信息交换接口实现类,默认为HeaderExchanger类实例,
     60             server = Exchangers.bind(url, requestHandler);
     61         } catch (RemotingException e) {
     62             throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
     63         }
     64         str = url.getParameter(Constants.CLIENT_KEY);
     65         if (str != null && str.length() > 0) {
     66             Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
     67             if (!supportedTypes.contains(str)) {
     68                 throw new RpcException("Unsupported client type: " + str);
     69             }
     70         }
     71         return server;
     72     }
     73 
     74     private void optimizeSerialization(URL url) throws RpcException {
     75         //得到序列化优化参数,通常为kryo或fst,将序列化实现设置为制定类实例
     76         String className = url.getParameter(Constants.OPTIMIZER_KEY, "");
     77         if (StringUtils.isEmpty(className) || optimizers.contains(className)) {
     78             return;
     79         }
     80         
     81         logger.info("Optimizing the serialization process for Kryo, FST, etc...");
     82         
     83         try {
     84             Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
     85             if (!SerializationOptimizer.class.isAssignableFrom(clazz)) {
     86                 throw new RpcException("The serialization optimizer " + className + " isn't an instance of " + SerializationOptimizer.class.getName());
     87             }
     88             
     89             SerializationOptimizer optimizer = (SerializationOptimizer) clazz.newInstance();
     90             
     91             if (optimizer.getSerializableClasses() == null) {
     92                 return;
     93             }
     94             
     95             for (Class c : optimizer.getSerializableClasses()) {
     96                 SerializableClassRegistry.registerClass(c);
     97             }
     98             
     99             optimizers.add(className);
    100         } catch (ClassNotFoundException e) {
    101             throw new RpcException("Cannot find the serialization optimizer class: " + className, e);
    102         } catch (InstantiationException e) {
    103             throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e);
    104         } catch (IllegalAccessException e) {
    105             throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e);
    106         }
    107     }

             (3)加载指定的序列化类

            调用optimizeSerialization(URL url)方法,将指定的序列化类加载到缓存,通常配置kryo, fst序列化,不配置,默认用hessian2序列化。

            2、实现refer()方法,此方法实现了服务消费者指向一个远程地址的代理接口(invoker,可能是一个伪装的集群或本地调用),返回Invoker对象,用于服务调用。

            实现逻辑为:

            (1)加载指定的序列化类

             调用optimizeSerialization(URL url)方法,将指定的序列化类加载到缓存,通常配置kryo, fst序列化,不配置,默认用hessian2序列化。

            (2)创建DubboInvoker对象,加入invokers内存缓存,返回dubboInvoker对象,结束。

    1     public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    2         optimizeSerialization(url);
    3         // create rpc invoker.
    4         DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
    5         invokers.add(invoker);
    6         return invoker;
    7     }

            (3)下面我们来分析创建DubboInvoker对象的过程。

            创建时调用如下语句:new DubboInvoker<T>(serviceType, url, getClients(url), invokers)。原始构造方法定义如下:

    1     public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers) {
    2         super(serviceType, url, new String[]{Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});
    3         this.clients = clients;
    4         // get version.
    5         this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");
    6         this.invokers = invokers;
    7     }

            传入四个参数:

            serviceType:要调用的服务接口;

            url:根据消费端声明确定的调用url;

            clients:用于底层远程通讯信息交换的客户端数组;

            invokers:DubboProtocol创建的所有invoker集合,此时共享给DubboInvoker对象;

            clients数组由DubboProtocol.refer()中使用getClients(url)得到,我们看看实现逻辑:

     1     private ExchangeClient[] getClients(URL url) {
     2         // 此变量用于判断是否是共享连接
     3         boolean service_share_connect = false;
    //得到url中的connections数量,默认为0,此设置的意义为限制客户端连接数量,具体含义见下面标注
    4 int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0); 5 // if not configured, connection is shared, otherwise, one connection for one service 6 if (connections == 0) { 7 service_share_connect = true; 8 connections = 1; 9 } 10 11 ExchangeClient[] clients = new ExchangeClient[connections]; 12 for (int i = 0; i < clients.length; i++) { 13 if (service_share_connect) { 14 clients[i] = getSharedClient(url); 15 } else { 16 clients[i] = initClient(url); 17 } 18 } 19 return clients; 20 }
    连接控制,此服务如果调用非常频繁,可以启用连接池,指定最大长连接数。
    客户端连接控制:限制客户端服务使用连接不能超过 10 个
    <dubbo:reference interface="com.foo.BarService" connections="10" /><dubbo:service interface="com.foo.BarService" connections="10" />
    
    服务端连接控制:限制服务器端接受的连接不能超过 10 个
    <dubbo:provider protocol="dubbo" accepts="10" /><dubbo:protocol name="dubbo" accepts="10" />
    如果是长连接,比如 Dubbo 协议,connections 表示该服务对每个提供者建立的长连接数,如果服务提供方和消费方都设置了,默认用覆盖策略,reference覆盖provider。
    getSharedClient(url)是得到共享客户端的方法,代码如下:
     1     /**
     2      * Get shared connection
     3      */
     4     private ExchangeClient getSharedClient(URL url) {
     5         String key = url.getAddress();
     6         ReferenceCountExchangeClient client = referenceClientMap.get(key);
     7         if (client != null) {
     8             if (!client.isClosed()) {
     9                 client.incrementAndGetCount();
    10                 return client;
    11             } else {
    12                 referenceClientMap.remove(key);
    13             }
    14         }
    15         synchronized (key.intern()) {
    16             ExchangeClient exchangeClient = initClient(url);
    17             client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
    18             referenceClientMap.put(key, client);
    19             ghostClientMap.remove(key);
    20             return client;
    21         }
    22     }
    initClient(url)是根据传入的url初始化连接客户端,代码如下:
     1 /**
     2      * Create new connection
     3      */
     4     private ExchangeClient initClient(URL url) {
     5 
     6         // client type setting.默认用netty
     7         String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
     8 
     9         String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
    10         boolean compatible = (version != null && version.startsWith("1.0."));
    11         url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
    12         // enable heartbeat by default
    13         url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
    14 
    15         // BIO is not allowed since it has severe performance issue.
    16         if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
    17             throw new RpcException("Unsupported client type: " + str + "," +
    18                     " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
    19         }
    20 
    21         ExchangeClient client;
    22         try {
    23             // connection should be lazy,创建可延迟连接的客户端
    24             if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
    25                 client = new LazyConnectExchangeClient(url, requestHandler);
    26             } else {
    27                 client = Exchangers.connect(url, requestHandler);
    28             }
    29         } catch (RemotingException e) {
    30             throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
    31         }
    32         return client;
    33     }
    3、 下面我们分析DubboProtocol对ExchangeHandler接口的实现逻辑,就是建立连接用到的requestHandler对象,它是对request处理的接口对象。以下代码把实现代码隐藏了。
     1     private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
     2         //核心方法,对request传来的message(即Invocation)的回复,即调用相应invoker的invoke(invocation)方法,返回调用结果
     3         public Object reply(ExchangeChannel channel, Object message) throws RemotingException {}
    32         //接受请求,如果message是一个Invocation,则调用reply()不返回结果,否则调用父类received(),其实什么也不干
    34 public void received(Channel channel, Object message) throws RemotingException {} 41 //调用onconnect设置的方法,即当连接成功时触发的方法
    43 public void connected(Channel channel) throws RemotingException {} 46 //调用disconnect设置的方法,即当连接关闭时触发的方法
    48 public void disconnected(Channel channel) throws RemotingException {} 54 //调用方法的实现,内部根据methodKey调用createInvocation()创建Invocation对象,调用received()方法 55 private void invoke(Channel channel, String methodKey) {} 65 //创建Invocation对象并返回,其中根据URL的属性和methedKey初始化Invocation对象 66 private Invocation createInvocation(Channel channel, URL url, String methodKey) {} 81 };
     1         public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
     2             if (message instanceof Invocation) {
     3                 Invocation inv = (Invocation) message;
    //从缓存中得到invoker对象
    4 Invoker<?> invoker = getInvoker(channel, inv); 5 // 如果设置了回调服务方法,比较设置的回调方法在invoker对象中是否声明了该方法 6 if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) { 7 String methodsStr = invoker.getUrl().getParameters().get("methods"); 8 boolean hasMethod = false; 9 if (methodsStr == null || methodsStr.indexOf(",") == -1) { 10 hasMethod = inv.getMethodName().equals(methodsStr); 11 } else { 12 String[] methods = methodsStr.split(","); 13 for (String method : methods) { 14 if (inv.getMethodName().equals(method)) { 15 hasMethod = true; 16 break; 17 } 18 } 19 } 20 if (!hasMethod) { 21 logger.warn(new IllegalStateException("The methodName " + inv.getMethodName() + " not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) + " ,invocation is :" + inv); 22 return null; 23 } 24 } 25 RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
    //调用invoke并返回结果
    26 return invoker.invoke(inv); 27 } 28 throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress()); 29 }

            代码实现中经常看见ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME)类似的语句,这是Dubbo默认的类动态加载实例化的方式SPI,替代了SpringBean管理机制,实现了IOC和AOP的功能。以后的章节会详细讨论。

            最后,我们分析一下实际的方法调用实现,即DubboInvoker.doInvoke()

     1     protected Result doInvoke(final Invocation invocation) throws Throwable {
     2         RpcInvocation inv = (RpcInvocation) invocation;
     3         final String methodName = RpcUtils.getMethodName(invocation);
     4         inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
     5         inv.setAttachment(Constants.VERSION_KEY, version);
     6 
     7         ExchangeClient currentClient;
     8         if (clients.length == 1) {
     9             currentClient = clients[0];
    10         } else {
    11             currentClient = clients[index.getAndIncrement() % clients.length];
    12         }
    13         try {
    14             boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
    15             boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
    16             int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
    17             if (isOneway) { //不需要返回值,返回空的RpcResult对象
    18                 boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
    19                 currentClient.send(inv, isSent);
    20                 RpcContext.getContext().setFuture(null);
    21                 return new RpcResult();
    22             } else if (isAsync) { //异步调用,调用后可以用RpcContext.getFuture()异步获取返回结果
    23                 ResponseFuture future = currentClient.request(inv, timeout);
    24                 RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
    25                 return new RpcResult();
    26             } else {  //默认是同步调用,需要有返回结果,此时要清空Future
    27                 RpcContext.getContext().setFuture(null);
    28                 return (Result) currentClient.request(inv, timeout).get();
    29             }
    30         } catch (TimeoutException e) {
    31             throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
    32         } catch (RemotingException e) {
    33             throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
    34         }
    35     }

            四、其他协议实现

            1、HessianProtocol实现

            以HTTP、HessianSkeleton为底层实现,实现了doExport()和doRefer()方法。

            Hessian协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。

            Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:

    • 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用
    • 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。

            特性

    • 连接个数:多连接
    • 连接方式:短连接
    • 传输协议:HTTP
    • 传输方式:同步传输
    • 序列化:Hessian二进制序列化
    • 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。
    • 适用场景:页面传输,文件传输,或与原生hessian服务互操作

            约束

    • 参数及返回值需实现 Serializable 接口
    • 参数及返回值不能自定义实现 ListMapNumberDateCalendar 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。

            配置

            <dubbo:protocol name="hessian" port="8080" server="jetty" />

             详见dubbo用户手册Hessian协议说明

            2、RmiProtocol实现

            以spring的Rmi框架为底层实现,实现了doExport()和doRefer()方法。

            RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。

            注意:如果正在使用 RMI 提供服务给外部访问 1,同时应用里依赖了老的 common-collections 包 2 的情况下,存在反序列化安全风险 3。

            特性

            连接个数:多连接
            连接方式:短连接
            传输协议:TCP
            传输方式:同步传输
            序列化:Java 标准二进制序列化
            适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。
            适用场景:常规远程服务方法调用,与原生RMI服务互操作

            约束

            参数及返回值需实现 Serializable 接口
            dubbo 配置中的超时时间对 RMI 无效,需使用 java 启动参数设置:-Dsun.rmi.transport.tcp.responseTimeout=3000,参见下面的 RMI 配置

            详情见dubbo用户手册RMI协议说明

            3、其他协议实现

            还实现了http协议,webservice协议,rest协议,thrift协议等,协议说明详见dubbo用户手册

  • 相关阅读:
    我的SICP习题解答-chapter 1
    redis主从复制实验,使用ruby
    python连接redis
    ruby连接redis
    python中的classmethod和staticmethod有什么不同[转载]
    安装配置rails环境
    试用memcached高可用repcached
    试用memcached
    Python一个很好玩的特性decorator
    mybatis随笔四之MapperProxy
  • 原文地址:https://www.cnblogs.com/markcd/p/8453260.html
Copyright © 2020-2023  润新知