热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dubbo——服务暴露的实现原理

配置承载初始化不管在服务暴露还是服务消费场景下,Dubbo框架都会根据优先级对配置信息做聚合处理,目前默认覆盖策略主要遵循以下几点规则:

配置承载初始化

不管在服务暴露还是服务消费场景下,Dubbo框架都会根据优先级对配置信息做聚合处理,目前默认覆盖策略主要遵循以下几点规则:

  1. -D 传递给JVM参数优先级最高,比如-Ddubbo.protocol.port=20880。
  2. 代码或XML配置优先级次高,比如Spring中XML文件制定
  3. 配置文件优先级最低,比如dubbo.properties文件制定dubbo.protocol.port=20880。

一般推荐使用dubbo.properties作为默认值,只有XML没有配置时,dubbo.properties配置项才会生效,通常用于共享配置,比如应用名等。

Dubbo的配置也会受到provider的影响,这个属于运行期属性值影响,同样遵循以下几点规则:

  1. 如果只有provider端指定配置,则会自动透传到客户端(比如timeout)。
  2. 如果客户端也配置了响应属性,则服务端配置会被覆盖(比如timeout)。

运行时属性随着框架特性可以动态添加,因此覆盖策略中包含的属性没办法全部列出来,一般不允许透传的属性都会在ClusterUtils#mergeUrl中进行处理。

远程服务的暴露机制

整体RPC的暴露原理:

ServiceConfig ------> ref↓ProxyFactory --------> Javassist、JDK动态代理↓Invoker ----------> AbstractProxyInvoker↓Protocol --------> Dubbo、injvm等↓Exporter

在整体上看,Dubbo框架做服务暴露分为两大部分,第一步将持有的服务实例通过代理转换成Invoker,第二步会把Invoker通过具体协议(比如Dubbo)转换成Exporter,框架做了这层抽象大大方便了功能扩展。这里的Invoker可以简单理解成一个真实的服务对象实例,是Dubbo框架实体域,所有模型都会向它靠拢,可向它发起invoer调用。它可能是本地的实现,也可能是一个远程的实现,还可能是一个集群的实现。

框架真正进行服务暴露的入口点在ServiceConfig#doExport中,无论XML还是注解,都会转换成ServiceBean,它集成自ServiceConfig,在服务暴露之前会按照上面的覆盖策略生效,主要处理思路就是遍历服务的所有方法,如果没有值则尝试从 -D 选项中读取,如果还没有则自动从配置文件dubbo.properties中读取。

Dubbo支持多注册中心同时写,如果配置了服务同时注册多个注册中心,则会在ServiceConfig#doExportUrls中依次暴露:
在这里插入图片描述
Dubbo也支持相同服务暴露多个协议,比如同时暴露Dubbo和REST协议,框架内部会依次对使用的协议都依次服务暴露,每个协议注册元数据都会写入多个注册中心。在①中会自动获取用户配置的注册中心,如果没有显示指定注册中心,则默认会用全局配置的注册中心。在②处理多协议服务暴露的场景,真实服务暴露逻辑是在doExportUrlsFor1Protocol方法中实现的:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 在①中主要通过反射获取配置对象并放到map中用于后续构造URL参数(比如应用名等)
  • 在②中主要区分全局配置,默认在属性前面增加default.前缀,当框架获取URL中的参数时,如果不存在则会自动尝试获取default. 前缀对应的值
  • 在③中主要处理本地内存JVM协议暴露
  • 在④中主要追加监控上报地址,框架会在拦截器中执行数据上报,这部分是可选的
  • 在⑤中会通过动态代理的方式创建Invoker对象,在服务端生成的是AbstractProxyInvoker实例,所有真实的方法调用都会委托给代理,然后代理转发给服务ref调用。目前框架实现两种代理:JavassistProxyFactory和JdkProxyFactory
    • JavassistProxyFactory模式原理:创建Wrapper子类,在子类中实现invokeMethod方法,方法体内会为每个ref方法都做方法名和方法参数匹配校验,如果匹配则直接调用即可,相比JDKProxyFactory省去了反射调用的开销。
    • JDKProxyFactory:通过反射获取真实对象的方法,然后调用即可。
  • 在⑥中主要先触发服务暴露(端口打开等),然后进行服务元数据注册
  • 在⑦中主要处理没有使用注册中心的场景,直接进行服务暴露不需要元数据注册,因为这里暴露的URL信息是以具体RPC协议开头的,并不是以注册中心协议开头的。

为了更容易地理解服务暴露于注册中心的干洗,以下列表分别展示有注册中心和无注册中心的URL:

  • registry://host:port/com.alibaba.dubbo.registry.RegistryService?protocol==zookeeper&export=dubbo://ip:port/xxx?..
  • dubbo://ip:host/xxx.Service?timeout=1000&..

protocol实例会自动根据服务暴露URL自动做适配,有注册中心场景会取出具体协议,比如Zookeeper,首先会创建注册中心实例,然后取出export对应的具体服务URL,最后用服务URL对应的协议(默认为Dubbo)进行服务暴露,当服务暴露成功后把服务数据注册到ZooKeeper。如果没有注册中心,则在⑦中会自动判断URL对应的协议(Dubbo)并直接暴露服务,从而没有经过注册中心。

将服务实例ref转换成Invoker之后,如果有注册中心时,则会通过RegistryProtocol#export进行更细粒度的控制,比如先进行服务暴露再注册服务元数据。注册中心在做服务暴露时依次做了一下几件事:

  1. 委托具体协议(Dubbo)进行服务暴露,创建NettyServer监听端口和保存服务实例。
  2. 创建注册中心对象,与注册中心创建TCP连接。
  3. 注册服务元数据到注册中心。
  4. 订阅configuators节点,监听服务动态属性变更事件。
  5. 服务销毁收尾工作,比如关闭端口、反注册服务信息等。

在这里插入图片描述
DestroyableExporter中重写的unexport()方法如下:
在这里插入图片描述

当服务真实调用时会触发各种拦截器Filter,这个是在哪里初始化的呢?在①中进行服务暴露前,框架会做拦截器初始化,Dubbo在加载protocol扩展点时会自动注入ProtocolListennerWrapper和ProtocolFilterWrapper

ProtocolFilterWrapper --> ProtocolListennerWrapper --> DubboProtocol

在ProtocolListenerWrapper实现中,在对服务提供者进行暴露时回调对应的监听器方法。ProtocolFilterWrapper会调用下一级ListenerExporterWrapper#export方法,在该方法内部会触发buildInvokerChain进行拦截器构造:
在这里插入图片描述
在这里插入图片描述

①:在触发Dubbo协议暴露前先对服务Invoker做了一层拦截器构建,在加载所有拦截器时会过滤只对provider生效的数据。

②: 首先获取真实服务ref对应的Invoker并挂载到整个拦截器链尾部,然后逐级包裹其他拦截器,这样保证了真实服务调用是最后触发的。

③:逐层转发拦截器服务调用,是否调用下一个拦截器由具体拦截器实现。

在构造调用拦截器之后会调用Dubbo协议进行服务暴露:
在这里插入图片描述
在这里插入图片描述
①和②:中主要根据服务分组、版本、服务接口和暴露端口作为key用于关联具体服务Invoker。
③:对服务暴露做校验判断,因为同一个协议暴露有很多接口,只有初次暴露的接口才需要打开端口监听。
然后在④中触发HeaderExchanger中绑定的方法,最后会调用底层NettyServer进行处理。在初始化Server过程中会初始化很多Handl用于支持一些特性,比如心跳、业务线程池处理编解码的Handler和响应方法调用的Handler。

本地服务的暴露机制

很多实用Dubbo框架的应用可能存在同一个JVM暴露了远程服务,同时同一个JVM内部又引用了自身服务的情况,Dubbo默认会把远程服务用injvm协议再暴露一份,这样消费方直接消费同一个JVM内部的服务,避免了跨网络进行远程通信。

在这里插入图片描述

通过exportLocal实现可以发现,在①中显式Dubbo指定用injvm协议暴露服务,这个协议比较特殊,不会做端口打开操作,仅仅把服务保存在内存中而已。在②中会提取URL中的协议,在InjvmProtocol类中存储服务实例信息,它的实现也是非常直接了当的,直接返回InjvmExporter实例对象,构造函数内部会把当前Invoker加入exporterMap:
在这里插入图片描述
在这里插入图片描述


推荐阅读
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • Ansibleplaybook roles安装redis实例(学习笔记二十九)
    1、相关redis参数:2、templatesredis.conf配置相关参数:daemonizeyespidfilevarrunredis_{{red ... [详细]
  • 人脸检测 pyqt+opencv+dlib
    一、实验目标绘制PyQT界面,调用摄像头显示人脸信息。在界面中,用户通过点击不同的按键可以实现多种功能:打开和关闭摄像头, ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • springboot启动不了_Spring Boot + MyBatis 多模块搭建教程
    作者:枫本非凡来源:www.cnblogs.comorzlinp9717399.html一、前言1、创建父工程最近公司项目准备开始重构,框 ... [详细]
author-avatar
hexin01
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有