在前面一篇博客中分享了 dubbo 在网络通信当中的 consumer 的发送以及接收原理。通过集群容错最终选择一个合适的 Invoke 通过 netty 直联调用 provider 的服务。众所周知, netty 是基于 Java Nio 的 Reactor 模型的异步网络通信框架,所以 dubbo 在 consumer 端把异步变成了同步。

大概总结了 consumer 的发送与接收原理,下面我们来讨论一下 dubbo 网络通信当中 provider 的接收与发送原理。这样就完成了 dubbo 架构图里面的 consumer 调用 provider 的过程.

本次是分析 dubbo 的 provider 的接收与发送原理,讨论包括以下几个点:

  • provider 接收 consumer 请求
  • provider 的扩展点调用
  • provider 响应 consumer 调用
  • dubbo 服务调用总结

1、provider 接收 consumer 请求

同 consumer 一样 provider 默认也是通过 netty 进行网络通信的。在之前的分析 dubbo 进行服务暴露(NettyServer#doOpen)的时候, 它是通过 Netty 进行服务暴露,添加了一个 dubbo 的自定义 netty 的 ChannelHandler 也就是 NettyServerHandler 来处理网络通信事件。下面我们来看一下 provider 是如何接收 consumer 发送过来请求的。

以上就是 provider 接收 consumer 端的调用图,可以发现其实是和 consumer 端接收 provider 端的类似都是通过自定义 netty 的 ChannelHandler 也就是 NettyServerHandler 来接收网络请求。并且同样的通过 dubbo 自定义的 ChannelHandler 来处理请求。下面我们还是来分析一下这些 dubbo 自定义 ChannelHandler 的作用:

  • MultiMessageHandler:支持 MultiMessage 消息处理,也就是多条消息处理。
  • HeartbeatHandler:netty 心条检测。如果心跳请求,发送心跳然后直接 return,如果是心跳响应直接 return。
  • AllChannelHandler:使用线程池通过 ChannelEventRunnable 工作类来处理网络事件。
  • DecodeHandler:解码 message,解析成 dubbo 中的 Request 对象
  • HeaderExchangeHandler:处理解析后的 consumer 端请求的 Request 信息,把请求信息传递到 DubboProtocol 并从 DubboExpoter 里面找到相应具体的 Invoke 进行服务调用(后面具体分析)。

其实可以看到 consumer 与 provider 接收网络请求都是通过自定义 netty 的 ChannelHandler。然后通过调用自定义 ChannelHandler#channelRead (其实是 ChannelHandler 的子接口 ChannelInboundHandler#channelRead )来接收并处理网络请求。在之前服务暴露分析的时候我们讲过AbstractProtocol#exporterMap 也就是 dubbo 在进行服务暴露的时候通过 AbstractProtocol#serviceKey 为 key 以 DubboExporter(Invoke 转化成 Exporter) 为 value 的服务接口暴露信息。然后把请求信息交给 DubboProtocol 根据 consumer 里面 Request 里面的 Invocation 请求信息获取到 DubboExporter。最后通过DubboExporter#getInvoker 获取暴露服务具体的服务实现,完成整个调用。

我们可以看到 consumer 与 provider 进行网络接收信息是类似的,相同点都是通过自定义的 ChannelHandler 来处理网络请求信息。通过 dubbo 这个自定义的 ChannelHandler 来适配不同的 Java Nio 框架,因为在 AbstractPeer 类中都持有 dubbo 自定义的这个 ChannelHandler 。 dubbo 默认使用的是 netty 作为 Nio 框架,通过配置 dubbo 还可以以 Mina 与 Grizzly 作为 Nio 框架。

这个就用到了 dubbo 的核心 SPI 平等的对待第三方框架。

上面我们讨论了相同点,下面我们来看一下 consumer 与 provider 接收网络请求的不同点:

  • consumer 接收的是 provider 端发送过来的 Response(响应信息),而 provider 是接收 consumer 端发送过来的 Request(请求信息)。
  • consumer 最后在 HeaderExchangeHandler 中调用 handleResponse 方法,而 provider 最在是在HeaderExchangeHandler 中调用 handleRequest 方法。
  • consumer 会默认会同步等待 provider 处理后的响应信息(也可以异步处理),而 provider 在处理完成之后就会同步的把响应请求发送给 consumer.

2、provider 的扩展点调用

与 consumer 引用服务一样, provider 在暴露服务的时候也会有扩展点。 就像 J2EE 调用 Servlet 的时候也可以通过 java.servlet.Filter 进行调用扩展,dubbo 在进行服务暴露方的时候也会有 dubbo 自己的 Filter 扩展。那么我们就来看一下在进行 Invoke 调用的时候 dubbo 都有哪些扩展:

可以看到默认情况下,dubbo 在进行服务暴露的时候会加上框架自定义的 7 个 Filter 扩展。下面就来简单描述一下这 7 个 Filter 的作用:

  • EchoFilter:回声测试,用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。
  • ClassLoaderFilter:
  • GenericFilter:实现泛化调用,泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成.比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。
  • TraceFilter:方法调用时间查探扩展器, 通过 TraceFilter#addTracer 添加需要查探类的方法与查探最大次数。当进行方法调用的时如果该方法的调用次数少于传递的最大次数就会把方法调用耗时发送给远程服务。
  • MonitorFilter:MonitorFilter 其实是在分析之前 dubbo monitor 的时候就进行了详细的分析。它主要是通过<dubbo:monitor protocol="registry" />来激活 providerconsumer 端的指标监控。
  • TimeoutFilter:如果调用时间超过设置的 timeout 就打印 Log,但是不要阻止服务器的运行。
  • ExceptionFilter:非检测的异常将会为 ERROR 级别记录在 Provider 端。非检测的异常是未在接口上声明的未经检查的异常.dubbo 会将在这在 API 包中未引入的异常包装到RuntimeException中。

以上就是 dubbo 框架在 provider 端的默认 Filter 扩展,当然如果你有需求也可以自定义 Filter 扩展。具体可以参考 dubbo 官网的 调用拦截扩展。

3、调用服务并响应 consumer

provider 端通过接收 consumer 的请求并且解码,然后调用 provider 的一系列自定义扩展。下面就是调用服务端暴露服务的真正实现了。在进行服务暴露的时候最终会调用 SPI 接口 ProxyFactory (默认是
JavassistProxyFactory) 来获取 Invoke。我们可以来看一下 dubbo 官网对于服务提供者暴露一个服务的详细过程:

下面我们来看一下 JavassistProxyFactory 的源代码:

JavassistProxyFactory .java

public class JavassistProxyFactory extends AbstractProxyFactory {@Override@SuppressWarnings("unchecked")public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));}@Overridepublic <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);return new AbstractProxyInvoker<T>(proxy, type, url) {@Overrideprotected Object doInvoke(T proxy, String methodName,Class<?>[] parameterTypes,Object[] arguments) throws Throwable {return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);}};}}

在这里需要说明 ProxyFactory#getInvoker 这个方法的三个请求参数:

  • proxy : 暴露接口服务的具体实现类,比如 dubbo-demo-provider 中的 org.apache.dubbo.demo.provider.DemoServiceImpl 实例对象。
  • type : 暴露接口服务的 Class 对象,比如 dubbo-demo-api 中的 org.apache.dubbo.demo.DemoService 的 Class 实例对象。
  • url : 暴露接口服务的配置信息。具体信息如下:
registry://localhost:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.75.1%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D192.168.75.1%26bind.port%3D20880%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D3900%26qos.port%3D22222%26side%3Dprovider%26timestamp%3D1530184958055&pid=3900&qos.port=22222&registry=zookeeper&timestamp=1530184958041

然后进行服务调用的时候最终就会调用到暴露接口服务的具体实现类,也就是 DemoServiceImpl。最终返回的结果如下:

HeaderExchangeHandler 再通过 DubboInvoke 调用到了暴露接口服务的真正实现,并获取到返回值时。它还需要通过 HeaderExchangeHandler 也就是它自身把响应发送给 consumer。具体的调用时序图如下:

可以看到 dubbo 在响应 consumer 时最终也是通过 netty 来进行网络通信的。

4、服务调用总结

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

  • 服务容器负责启动,加载,运行服务提供者。
  • 服务提供者在启动时,向注册中心注册自己提供的服务。
  • 服务消费者在启动时,向注册中心订阅自己所需的服务。
  • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

当分析了整个 dubbo 从服务暴露到服务引用,然后再分析了 dubbo 的集群调用 以及 consumer 与 provider 的调用细节之后。再来看 dubbo 的调用图是不是另外有一番滋味。

参考资料:

  • http://dubbo.apache.org/books/dubbo-user-book/demos/echo-service.html
  • http://dubbo.apache.org/books/dubbo-user-book/demos/generic-service.html
  • http://dubbo.apache.org/books/dubbo-admin-book/install/simple-monitor-center.html
  • http://dubbo.apache.org/books/dubbo-dev-book/impls/filter.html
  • http://dubbo.apache.org/books/dubbo-dev-book/implementation.html
  • http://dubbo.apache.org/books/dubbo-user-book/preface/architecture.html

dubbo源码分析23 -- provider 接收与发送原理相关推荐

  1. apache dubbo 源码分析系列汇总

    Dubbo(读音[ˈdʌbəʊ])是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成.后面捐献给了知名的开源社区 ...

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

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

  3. Dubbo 源码分析 - 集群容错之 LoadBalance

    1.简介 LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载"均摊"到不同的机器上.避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况.通 ...

  4. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

  5. Dubbo 源码分析 - 集群容错之 Router

    1. 简介 上一篇文章分析了集群容错的第一部分 – 服务目录 Directory.服务目录在刷新 Invoker 列表的过程中,会通过 Router 进行服务路由.上一篇文章关于服务路由相关逻辑没有细 ...

  6. 志宇-dubbo源码分析

    dubbo源码分析 文档 dubbo加载配置文件 dubboSPI dubbo服务提供 1.校验配置信息 2.创建URL 3.本地注册 4.远程注册 4.1 开启netty服务端 4.2 连接注册中心 ...

  7. 精尽 Dubbo 源码分析 —— API 配置

    1. 概述 Dubbo 的配置目前提供了四种配置方式:1. API 配置 2. 属性配置 3. XML 配置 4. 注解配置 2. 配置一览 我们来看看 dubbo-config-api 的项目结构, ...

  8. dubbo源码分析7 之 服务本地暴露

    在上一篇文章我们分析了一下 dubbo 在服务暴露发生了哪些事,今天我们就来分析一下整个服务暴露中的本地暴露.(PS:其实我感觉本地暴露蛮鸡肋的).本地暴露需要服务提供方与服务消费方在同一个 JVM. ...

  9. Dubbo源码分析:小白入门篇

    关注公众号"java后端技术全栈" 回复"000"获取优质面试资料 大家好,我是老田 答应了小伙伴的Dubbo源码分析系列,今天终于来了,希望不是很晚. 主要也 ...

  10. dubbo源码分析系列(1)扩展机制的实现

    1 系列目录 dubbo源码分析系列(1)扩展机制的实现 dubbo源码分析系列(2)服务的发布 dubbo源码分析系列(3)服务的引用 dubbo源码分析系列(4)dubbo通信设计 2 SPI扩展 ...

最新文章

  1. Spring事务——Spring 2.X的事务配置策略
  2. 工具使用 - Quartus II 管脚分配方法
  3. 中国交通节能减排行业运营效益状况及十四五建设格局分析报告2021-2027年
  4. CVPR 2020 Oral 汇总:论文 / 代码 / 解读(更新中)
  5. WF 创建 SQL 持久性数据库
  6. DEDE 字符串操作常见问题
  7. vue检测不到data里数组里面元素的变化
  8. mac中修改系统限制量--ulimit和sysctl
  9. Splay初步【bzoj1503】
  10. 图解Javascript——作用域、作用域链、闭包
  11. To disable deprecation,,use _CRT_SECURE_NO_WARNINGS
  12. 互联网日报 | 5月25日 星期二 | 华为再次重申不造车;贝壳宣布左晖为公司“永远的荣誉董事长”;清华大学成立量子信息班...
  13. 基于FPGA实现uart串口模块(Verilog)--------接收模块及思路总结
  14. JMeter测试实例
  15. keytool 错误: java.io.IOException: Keystore was tampered with, or password was incorrect → 解决办法
  16. 数据库:候选码、主码、超码、外码、主属性、非主属性
  17. VB代码 VB小程序
  18. 大数据清洗、转换工具——ETL工具概述
  19. 中国传统色的雅称——你可知?
  20. 系列篇|结构光三维重建基本原理

热门文章

  1. C++ 关于ShowWindow()的疑问
  2. Beyond Compare 提示“缺少评估信息或损坏”
  3. 思科 | 无线局域网组网实验
  4. 电子信息类包含计算机科学与技术么,电子信息类和计算机类有什么区别
  5. 你别不信,安卓机用户才是苹果机涨价的最大受害者
  6. C# 实验五 银行系统
  7. Elasticsearch-SERVICE_UNAVAILABLE/1/state not recovered
  8. QGraphicsItem限制拖动方向和位置
  9. 代码——几种常见的空格符号
  10. 如何应用计算机键盘截图,键盘怎么截图