Dubbo的RPC调用过程

Dubbo的RPC调用在客户端触发,配置文件中定义:

<dubbo:reference id="xxxService" interface="xxx.xxx.Service"/>

这一行定义会为xxx.xxx.Service在本地生成一个远程代理。在Dubbo中这个代理用com.alibaba.dubbo.common.bytecode.proxy0的实例表示。这个代理存在于本地可以像本地Bean一样调用该服务,具体通信过程由代理负责。

代理实例仅仅包含一个handler对象(实现InvokerInvocationHandler接口),handler中包含RPC调用核心接口Invoker < T >的实现。

public interface Invoker<T> extends Node {
    Class<T> getInterface();
    //调用过程的具体表示形式
    Result invoke(Invocation invocation) throws
    RpcException;
}

其中核心方法invoker(Invocation invocation)参数Invocation是一个调用过程的抽象,也是Dubbo框架的核心接口。

public interface Invocation { 
//调用的方法名称
String getMethodName(); 
//调用方法的参数的类型列表 
Class<?>[] getParameterTypes(); 
//调用方法的参数列表
Object[] getArguments();
//调用时附加的数据,用 map 存储
Map<String, String> getAttachments();
//根据 key 来获取附加的数据
String getAttachment(String key); 
//getAttachment(String key)的拓展,支持默认值获取 
String getAttachment(String key, String defaultValue); 
//获取真实的调用者实现
Invoker<?> getInvoker();
}

Invocation和Invoker相互依存,如果是RPC调用,Invocation的具体实现就是RPCInvocation,该方法异常时抛出RpcException。

代理的handler实例中实现Invoker< T >的接口的是MockClusterInvoker,它仅仅是一个Invoker的包装,只是用于Dubbo框架的mock功能,它的invoke方法实现:

public Result invoke(Invocation invocation) throws RpcException {
    Result result = null;
    /*这一行代码用于获取该服务是否 供 mock 功能,如果 供,则 url 中会包 含 mock 关键字*/
    String value = directory.getUrl().getMethodParameter(invocation. getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
    if (value.length() == 0 || value.equalsIgnoreCase("false")){
        //没有 mock 的过程,直接调用
        result = this.invoker.invoke(invocation);
    } else if (value.startsWith("force")) {
        //日志记录代码部分省略
        //force:direct mock,这里用于处理强制 mock 的情况,不执行远程调用 
        result = doMockInvoke(invocation, null);
    } else {
        //fail-mock,这里处理失败后 mock 的情况,即出现调用异常时执行 mock 
        try {
            result = this.invoker.invoke(invocation);               } catch (RpcException e) {
            if (e.isBiz()) { throw e;
            } else {
                //省略日志记录部分的代码
                //这里执行 mockInvoke 的逻辑,在本地伪装结果 
                result = doMockInvoke(invocation, e);
            } 
        }
}
return result; }

MockClusterInvoker实现来Invoker< T >接口但是没有实现invoke的逻辑,只是包装了一个Invoker的真实实现。如下:

public class MockClusterInvoker<T> implements Invoker<T>{
    private final Directory<T> directory ;
    private final Invoker<T> invoker;
    public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
        this.directory = directory;
        this.invoker = invoker; }
}

其中真实的Invoker实现包含了集群容错的功能,可知Dubbo的集群容错是在Invoker中实现的。

Dubbo默认的容错实现FailoverClusterInvoker即失败重试。在FailoverClusterInvoker的实现中核心方法invoke(Invocation invocation)继承自AbstractClusterInvoker< T > 抽象类。AbstractClusterInvoker< T >中的invoke(Invocation invocation)方法的实现如下:

public Result invoke(final Invocation invocation) throws RpcException { 
    checkWheatherDestoried();
    LoadBalance loadbalance;
    List<Invoker<T>> invokers = list(invocation);
    if (invokers != null && invokers.size() > 0) {
        //这里省略了获取 loadbalance 实例的实现代码,具体思路就是用拓展类加载器根据配置加载 loadbalance 实例
    } else {
        loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).
getExtension(Constants.DEFAULT_LOADBALANCE);
    }
    RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); 
    return doInvoke(invocation, invokers, loadbalance);
}

list(invocation) 获取 Invoker 的列表List< Invoker< T > >,这个列表可以对应到服务 供者的列表。其中,list(invocation)方法的实现代码如下:

protected List<Invoker<T>> list(Invocation invocation) throws RpcException { 
    List<Invoker<T>> invokers = directory.list(invocation);
    return invokers;
}

list(invocation)方法中用到关键的接口 Directory,它是 Dubbo 框架中用于封装服务供者列表的一个数据结构。
Directory 的定义代码如下:

public interface Directory<T> extends Node {
    Class<T> getInterface();
    List<Invoker<T>> list(Invocation invocation) throws RpcException; 
    }

list(Invocation invocation)的参数是一个Invocation 对象表示的是一次(远程)调用过程,list 方法返回一个调用者的列表 List< Invoker< T > >,directory 接口的实例实例是 RegistryDirectory类的对象 ,RegistryDirectory的list(invocation)方法继承自 AbstractDirectory,具体实现如下:

public List<Invoker<T>> list(Invocation invocation) throws RpcException { 
    if (destroyed)
throw new RpcException("Directory already destroyed .url: "+ getUrl()); 
    //获取调用列表
    List<Invoker<T>> invokers = doList(invocation);
    List<Router> localRouters = this.routers; // local reference
    if (localRouters != null && localRouters.size() > 0) {
        for (Router router: localRouters){ 
            try {
                if (router.getUrl() == null || router.getUrl(). getParameter(Constants.RUNTIME_KEY, true)) {
                    invokers = router.route(invokers, getConsumerUrl(), invocation);
                }
            } catch (Throwable t) {
                logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
            }
         }
    }
    return invokers;
}

每个 Consumer 会在本地缓存(或者从注册中心获取)路由集合 List< Router > localRouters,然后判断集合中的每一个路由规则是否可以对当前调 用进行过滤,如果路由规则符合当前调用,就对调用列表 List< Invoker< T > >进行 筛选,去除不符合的调用者。
Router 接口的定义代码如下:

public interface Router extends Comparable<Router> {
    //获取当前路由的 url
    URL getUrl();
    //路由方法,对传入的 List<Invoker<T>>进行路由筛选,筛选的条件包括 Invocation 对象和 consumer 的 url
    <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值