dubbo的核心技术--RPC调用:分为俩部分RPC协议Protocol和方法调用Invoke;

一、RPC协议Protocol(Remote Procedure Call)远程过程调用协议

1、我们平时使用最多的http协议其实也属于RPC协议,下图分别是普通的传输层TCP和应用层http与dubbo优化后的TCP和dubbo协议进行对比。

总结:

原生的传输层协议(TCP)需要网络三次握手和四次挥手,客户端与服务端的建立链接成本过高,dubbo对TCP进行优化,实现单一长连接,降低网络链接成本;

原生的应用层协议(http)通用性高、拓展性强,同时协议复杂解析成本高,dubbo消费者调用服务者时只需要传输很少字节,并且结构简单,便与解析;

2、dubbo远程同步调用原理分析:

Dubbo默认协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。Dubbo缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。

dubbo缺省协议,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。

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

为什么采用异步单一长连接:因为服务的现状大都是服务提供者少,而服务的消费者多,通过单一长连接,保证单一消费者不会压死提供者,长连接减少连接握手验证,并使用异步IO,复用线程池,降低网络链接成本。

为什么要消费者比提供者个数多:因为dubbo协议采用单一长连接,假设网络为千兆网卡(1024Mbit=128MByte),根据测试经验数据每条连接最多只能压满7MByte(不同的网络环境可能不一样,仅供参考),理论上1个服务提供者需要20个服务消费者才能压满网卡。

为什么不能传大包:如果每次请求的数据包大小为500KByte,则单个服务提供者的TPS(每秒处理事务数)最大为:128Mbyte/500KByte=262. 单个消费者调用单个服务提供者的TPS(每秒处理事务数)最大为: 7MByte/500KByte = 14。可看出网络将成为瓶颈。

通常,一个典型的同步远程调用应该是这样的:

1、客户端线程调用远程接口,向服务端发送请求,同时当前线程应该处于“暂停“状态,即线程不能向后执行了,必需要拿到服务端给自己的结果后才能向后执行;
2、服务端接到客户端请求后,处理请求,将结果给客户端;
3、客户端收到结果,然后当前线程继续往后执行;

Dubbo底层使用Socket发送消息的形式进行数据传递,结合了mina框架,使用IoSession.write()方法,这个方法调用后对于整个远程调用(从发出请求到接收到结果)来说是一个异步的,即对于当前线程来说,将请求发送出来,线程就可以往后执行了,至于服务端的结果,是服务端处理完成后,再以消息的形式发送给客户端的。----红字部分我没有搞明白!!

于是这里出现了2个问题:
  • 当前线程怎么让它“暂停”,等结果回来后,再向后执行?
  • 正如前面所说,Socket通信是一个全双工的方式,如果有多个线程同时进行远程方法调用,这时建立在client server之间的socket连接上会有很多双方发送的消息传递,前后顺序也可能是乱七八糟的,server处理完结果后,将结果消息发送给client,client收到很多消息,怎么知道哪个消息结果是原先哪个线程调用的?
基本原理如下:
1.client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用AtomicLong从0开始累计数字的。
2.将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object。
3.向专门存放调用信息的全局ConcurrentHashMap中put(Id,Object)。
4.将ID和打包的方法调用信息封装成以对象connRequest,使用IoSession.write(connRequest)异步发送出去。
5.当前线程再使用callback的get()方法视图获取远程方法返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁,再先检测是否已经获取到结果,如果没有,就调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。
6.服务端接收到请求并处理后,将结果(此结果中包含了签名的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的ConcurrentHashMap中get(Id),从而找到callback,将方法调用结果设置到callback对象里。
7.监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已经释放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束。
需要注意的是,这里的callback对象是每次调用产生一个新的,不能共享,否则会有问题;另外ID必需至少保证在一个Socket连接里面是唯一的。
 
现在,前面两个问题已经有答案了:
  • 当前线程怎么让它“暂停”,等结果回来后,再向后执行?

答:先生成一个对象obj,在一个全局map里put(ID,obj)存放起来,再用synchronized获取obj锁,再调用obj.wait()让当前线程处于等待状态,然后另一消息监听线程等到服务端结果来了后,再map.get(ID)找到obj,再用synchronized获取obj锁,再调用obj.notifyAll()唤醒前面处于等待状态的线程。

  • 正如前面所说,Socket通信是一个全双工的方式,如果有多个线程同时进行远程方法调用,这时建立在client server之间的socket连接上会有很多双方发送的消息传递,前后顺序也可能是乱七八糟的,server处理完结果后,将结果消息发送给client,client收到很多消息,怎么知道哪个消息结果是原先哪个线程调用的?
     答:使用一个ID,让其唯一,然后传递给服务端,再服务端又回传回来,这样就知道结果是原先哪个线程的了。

Dubbo系列(三)dubbo的核心技术--RPC调用的更多相关文章

  1. dubbo系列三、架构介绍及各模块关系

    一.整体设计 图例说明: 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口. 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代 ...

  2. Python操作rabbitmq系列(六):进行RPC调用

    此刻,我们已经进入第6章,是官方的最后一个环节,但是,并非本系列的最后一个环节.因为在实战中还有一些经验教训,并没体现出来.由于马上要给同事没培训celery了.我也来不及写太多.等后面,我们再慢慢补 ...

  3. Spring Cloud 系列之 Dubbo RPC 通信

    Dubbo 介绍 官网:http://dubbo.apache.org/zh-cn/ Github:https://github.com/apache/dubbo 2018 年 2 月 15 日,阿里 ...

  4. SpringCloud Alibaba实战(12:引入Dubbo实现RPC调用)

    源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 大家好,我是老三,断更了半年,我又滚回来继续写这个系列了,还有人看吗-- 在前面的章 ...

  5. [转载] Dubbo实现RPC调用使用入门

    转载自http://shiyanjun.cn/archives/341.html 使用Dubbo进行远程调用实现服务交互,它支持多种协议,如Hessian.HTTP.RMI.Memcached.Red ...

  6. dubbo rpc调用抛出的Exception处理

    关于dubbo的Exception堆栈被吃处理,网上已经有比较多的解决方法,在我们的应用场景中,不希望RPC调用对方抛出业务exception,而是通过Resp中的errorCode,errorMsg ...

  7. 【原】通过Dubbo注解实现RPC调用

    启动Dubbo服务有2个方式,1是通过xml配置,2是通过注解来实现,这点和Spring相似. 采用XML配置如下: <?xml version="1.0" encoding ...

  8. Dubbo通过注解实现RPC调用

    启动Dubbo服务有2个方式,1是通过xml配置,2是通过注解来实现,这点和Spring相似. 采用XML配置如下:  <?xml version="1.0" encodin ...

  9. Dubbo实现RPC调用使用入门

    使用Dubbo进行远程调用实现服务交互,它支持多种协议,如Hessian.HTTP.RMI.Memcached.Redis.Thrift等等.由于Dubbo将这些协议的实现进行了封装了,无论是服务端( ...

随机推荐

  1. TeamCity安装和配置

    目录 TeamCity安装和配置 前言 持续集成(CI) TeamCity 环境 安装 配置TeamCity 项目构建 效果展示 TeamCity自动构建项目及集成IDEA(待更新......) 结束 ...

  2. pytorch中使用多显卡训练以及训练时报错:expect more than 1 value per channel when training, got input size..

    pytorch在训练中使用多卡: conf.device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu&quo ...

  3. 如何自定义starter

    在springboot启动流程的系列文章中,我们看过了springboot的自动配置机制,本文将基于自动配置机制自定义一个自动配置的starter示例 正文 模块结构 首先,我们准备两个模块servi ...

  4. Recastnavigation 创建 off-mesh link 的潜规则

    Recastnavigation 在创建off-mesh link 时,发现有的off-mesh link 无法寻路(虽然在地图上能看到off-mesh link 的连线   在Google Grou ...

  5. js之split拆分字符串

    js之split拆分字符串 1.单字符拆分 let arr = str.split(',') 2.多字符拆分 let arr = str.split(/[(),]/)

  6. vue 2.0 + 如何实现加入购物车,小球飞入的动画

    github源码地址:https://github.com/13476075014/node-vue/tree/master/mynodeproject/13.sell/sell 在移动端经常会有加入 ...

  7. Python处理session最简单的方法

    前言: 不管是在做接口自动化还是在做UI自动化,测试人员遇到的第一个问题都是卡在登录上. 那是因为在执行登录的时候,服务端会有一种叫做session的会话机制. 一个很简单的例子: 在做功能测试的时候 ...

  8. 逐步探究ObjC的Weak技术底层

    前言 之前的文章有说过 Atomic 原子操作的原理,其作为一个特殊的修饰前缀,影响了存取操作. 在属性修饰定义中,还有另一类修饰前缀,他们分别是 strong weak assign copy,这些 ...

  9. SAP WebIDE里UI5应用的隐藏文件project.json

    在SAP WebIDE UI5应用编辑器里的菜单View->Show Hidden files点击后,即可发现项目文件夹下有一个隐藏文件project.json: 内容如下: 这也解释了为什么b ...

  10. KVM之磁盘管理工具qemu-img小结

    基本语法: qemu-img command [command options] 主要参数: info : 查看镜像的信息: create: 创建镜像: check: 检查镜像: convert: 转 ...