dubbo的远程调用过程是怎么样的?

dubbo远程过程调用经过了那些处理?

发起远程调用的时候究竟传了什么数据给provider?

要解决这些问题,欢迎一起探讨走进dubbo源码栏目。

在service reference中说了consumer端发起调用的时候使用的是远程服务的本地代理,发起调用的堆栈是

(上面调用堆栈中的filter链先不介绍了,留在后面service reply中介绍,因为consumer和provider的filter链构造过程是类似的)

前面说过consumer在引用服务的时候最终会生成一个proxy,该proxy是实现了对应的服务接口(比如:com.test.service.TestDubboService),而且包含一个InvokerInvocationHandler属性,在proxy的服务接口方法中调用InvokerInvocationHandler.invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
// 判断是否是Object的方法,如果是则直接执行本地调用,不发起远程调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
// toString、hashCode、equals执行本地调用
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
} // RpcInvocation是一个很重要的类,是真正在consumer和provider之间通过网络传输的实体,里面包含了服务调用需要的必要信息
RpcInvocation invocation = new RpcInvocation(method, args);
Result r = null;
try {
r = invoker.invoke(invocation);
Object o = r.recreate();
return o;
} catch (Throwable e) {
// ... 省略中间代码
}
} public class RpcInvocation implements Invocation, Serializable { private static final long serialVersionUID = -4355285085441097045L;
// 调用的方法全限定名
private String methodName;
// 方法的参数类型
private Class<?>[] parameterTypes;
// 入参
private Object[] arguments;
// 附件,可以传递一些其他信息
private Map<String, String> attachments;
// invoker
private transient Invoker<?> invoker;
// ... 省略中间代码
}

上面通过invoke调用发起invoker的调动链,依次调用invoker.invoke方法

MockClusterInvoker#invoke

invoke方法里面会判断是否配置了mock参数

  1. 如果没有配置则直接继续调用invoke
  2. 如果配置的值以force开头,表明直接mock调用
  3. 如果mock配置了但是不以force开头,要先试试正常调用,如果正常调用失败了才会使用mock

AbstractClusterInvoker#invoke

这里继续正常情况下的非mock调用。其实上面接下来调用的是FailbackClusterInvoker#invoke,但是FailbackClusterInvoker继承了AbstractClusterInvoker,而且FailbackClusterInvoker没有实现invoke方法,所以直接调用了超类的invoke方法

  1. 判断当前Invoker是否被销毁,如果销毁直接抛出异常
  2. 然后调用directory.list找出所有的invoker
  3. 通过SPI加载负载均衡的扩展
  4. 调用实现类的doInvoke

FailbackClusterInvoker#doInvoke

这个类是负责dubbo调用失败重试的类

  1. 首先检查是否有可以调用的invoker
  2. 调用AbstractClusterInvoker.select,通过路由到一个指定invoker
  3. 通过路由到的invoker发起调用
  4. 如果调用抛出异常了,将请求加入失败列表然后定时重试

上面invoker链调动完之后,会调用dubbo的filter链

// dubbo有一个调用上下文RpcContext,这个filter就是负责往context写信息
com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
// dubbo支持事件通知(oninvoke,onreturn,onthrow),通过该filter实现,
com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
// 负责调用过程的监控,调用耗时、并发数等
com.alibaba.dubbo.monitor.support.MonitorFilter

前面部分都是前置处理,开始通过网络向远程发起调用是DubboInvoker.doInvoke方法

protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
// 往invocation的attachments中写入path和version参数
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version); ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
// 因为dubbo支持多个client连接同一个provider,也就是是同一个provider多个连接connections,所以可能是有多个client
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
// 是否是异步调用,异步调用通过RpcContext.getContext().getFuture().get()来获取结果
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
// Oneway:单向调用,调用方只管发起调用,不需要知道返回值,直接返回
// Twoway:双向调用,调用方发起调用后需要知道返回值,需要返回值的情况又分为同步等待或者是异步通知
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
// 方法调用的超时时间
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
// oneWay只管发送,不关心结果,直接返回
return new RpcResult();
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout) ;
// 异步调用,直接返回,由future来接收结果,需要结果的时候从future中get
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
// 同步调用
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
// 这儿的异常是dubbo调用过程中dubbo本身的异常,并不是应用抛出的异常
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
}

那究竟最后consumer通过网络向provider传输过去的是什东西呢?下面这个方法属于HeaderExchangeChannel类

public ResponseFuture request(Object request, int timeout) throws RemotingException {
if (closed) {
throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
}
// create request.
// 这儿的Request就是consumer发送出去的内容,Request.data就是RpcInvocation,也就是说provider通过netty接收到的对象就是Request
Request req = new Request();
req.setVersion("2.0.0");
req.setTwoWay(true);
req.setData(request);
DefaultFuture future = new DefaultFuture(channel, req, timeout);
try{
//
channel.send(req);
}catch (RemotingException e) {
future.cancel();
throw e;
}
return future;
}

至于dubbo中的网络层netty这里就不深入介绍了,netty又是一个很强大的框架,以后专门介绍吧。

总结

至此,前面说的三个问题都解决了。consumer已经向provider发出了请求,接下来就是provider响应请求了。

dubbo源码—Service Invoke的更多相关文章

  1. dubbo源码—Service Reply

    dubbo通过netty将请求发送到provider的时候,provider之前已经启动好的NettyServer监听指定端口的时候会收到来自consumer的请求,将通过网络发送来的二进制编码成Re ...

  2. dubbo源码—service reference

    service reference 在编写好服务之后,dubbo会将服务export出去,这个时候就可以编写consumer来调用这个服务了.dubbo作为一个rpc框架,使用者使用远程服务和使用本地 ...

  3. dubbo源码—service export

    在应用编写好服务并进行之后,dubbo负责将服务export出去,dubbo export服务的时候主要做了以下几件事: 将服务export到本地(根据scope的配置) 创建Invoker(启动本地 ...

  4. Dubbo 源码分析 - 服务调用过程

    注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 官方文档中阅读本系列文章. 1. 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与引入.以及集群容错方面的代码.经过 ...

  5. Dubbo 源码分析 - 服务导出

    1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可 ...

  6. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  7. dubbo源码之服务发布与注册

    服务端发布流程: dubbo 是基于 spring 配置来实现服务的发布的,对于dubbo 配置文件中看到的<dubbo:service>等标签都是服务发布的重要配置 ,对于这些提供可配置 ...

  8. 【Dubbo 源码解析】07_Dubbo 重试机制

    Dubbo 重试机制 通过前面 Dubbo 服务发现&引用 的分析,我们知道,Dubbo 的重试机制是通过 com.alibaba.dubbo.rpc.cluster.support.Fail ...

  9. 【Dubbo 源码解析】05_Dubbo 服务发现&引用

    Dubbo 服务发现&引用 Dubbo 引用的服务消费者最终会构造成一个 Spring 的 Bean,具体是通过 ReferenceBean 来实现的.它是一个 FactoryBean,所有的 ...

随机推荐

  1. iOS Label 自适应高度

    推荐第二个 测试一,只改变numberOfLines属性,label的高度不会自适应(会有text中的一部分内容称为......) NSString *str = @"jgreijgirje ...

  2. whereis 命令详解

    whereis 作用:whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s).如果省略参数,则返回所有信息.  参数:-b 定位可 ...

  3. Python图片爬虫

    1.今天给大家介绍自己写的一个图片爬虫,说白了就是从网页自动上下载需要的图片 2.首先选取目标为:http://www.zhangzishi.cc/涨姿势这个网站如下图,我们的目标就是爬取该网站福利社 ...

  4. jquery学习总结(超级详细)

    本文仅针对jquery的部分知识点做总结,更为全面的可以去官网看中文文档.可以更为详细的了解jquery及其特性.       window.onload $(document).ready() 执行 ...

  5. Java的静态代码块是否会在类被加载时自动执行?

    JAVA静态代码块会在类被加载时自动执行? 一.先看Java静态方法,静态变量 http://www.cnblogs.com/winterfells/p/7906078.html 静态代码块 在类中, ...

  6. 解决ios手机上传竖拍照片旋转90度问题

    html5+canvas进行移动端手机照片上传时,发现ios手机上传竖拍照片会逆时针旋转90度,横拍照片无此问题:Android手机没这个问题. 因此解决这个问题的思路是:获取到照片拍摄的方向角,对非 ...

  7. 使用alembic进行数据库版本管理

    前言 随着项目业务需求的不断变更,数据库的表结构修改难以避免,此时就需要对数据库的修改加以记录和控制,便于项目的版本管理和随意的升级和降级. Alembic就可以很好的解决这个问题.Alembic是S ...

  8. 只要发生ajax请求时加载旋转的按钮

    定义一个变量 全局 c 只要发生ajax时给c++ 当ajax请求success或者error时,c--; 对加载的按钮添加个事件   监听 c  如果c得值没变化  则隐藏按钮   如果变化了则显示 ...

  9. TurnipBit—MicroPython开发板:从积木式编程语言开始学做小小创客

    编程.建模.制作动画和游戏--这些当初我们默认只有成年人玩得转的事情,现在早已经被无数小孩子给颠覆甚至玩出新境界了.热爱科技和动手的"创客"(Maker)现在在全世界都炙手可热.今 ...

  10. Python import其他层级的模块

    [前言] Python的文件目录结构虽然层次清晰,结构清楚,但是在调用的时候可能还是出现各式各样的找不到路径的错误. [模块导入] 1.导入上一级目录的模块 python中导入上一级目录的模块有两种方 ...