1. Dubbo

1.1. 是什么

Dubbo 是一款高性能、轻量级的开源 RPC 框架。由 10 层模式构成,整个分层依赖由上至下。通过这张图我们也可以将 Dubbo 理解为三层模式:

image.png

第一层的 Business 业务逻辑层由我们自己来提供接口和实现还有一些配置信息。
第二层的 RPC 调用的核心层负责封装和实现整个 RPC 的调用过程、负载均衡、集群容错、代理等核心功能。
第三层的 Remoting 则是对网络传输协议和数据转换的封装。

1.2. 核心能力 (特性)

image.png

1.3. 架构

image.png

1.4. 协议

1.4.1. Dubbo 协议

Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。

反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。

缺省协议,使用基于 netty 3.2.5.Final 和 hessian2 3.2.1-fixed-2(Alibaba embed version) 的 tbremoting 交互。

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

1.4.1.1. 为什么不适合大文件

https://blog.csdn.net/qq_43842093/article/details/126332133

1.4.2. 其他协议

通信框架方面,Dubbo 默认采用了 Netty 作为通信框架。
通信协议方面,Dubbo 除了支持私有的 Dubbo 协议外,还支持 RMI 协议、Hession 协议、HTTP 协议、Thrift 协议等。
序列化格式方面,Dubbo 支持多种序列化格式,比如 Dubbo、Hession、JSON、 Kryo、FST 等。

2. 工作原理

image.png

  1. 服务启动的时候,provider 和 consumer 根据配置信息,连接到注册中心 register,分别向注册中心注册和订阅服务
  2. register 根据服务订阅关系,返回 provider 信息到 consumer,同时 consumer 会把 provider 信息缓存到本地。如果信息有变更,consumer 会收到来自 register 的推送
  3. consumer 生成代理对象,同时根据负载均衡策略,选择一台 provider ,同时定时向 monitor 记录接口的调用次数和时间信息
  4. consumer 拿到代理对象之后,通过代理对象发起接口调用
  5. provider 收到请求后对数据进行反序列化,然后通过代理调用具体的接口实现

2.1. 负载均衡⭐️🔴

image-20211002222615305

2.2. 超时与重试

image.png

默认使用 consumer 中的 timeout 为 1000ms

image.png

该数字不包含刚开始的 1 次

2.2.1. 配置优先级

image.png
image.png

2.3. 服务容错⭐️🔴

https://www.bilibili.com/video/BV1t7411j7P7?p=15

image-20211018060627326

image-20211002223619323

2.4. 服务降级

image-20211002223240884

2.5. 延迟暴露

image-20211018060908252

2.6. 线程模型

image-20211018061104632

3. 注册中心

image-20200422011315464

image-20200422011522254

image-20200422011945717

3.1. 配置方式

image-20200422084117654

image-20200422084312824

image-20200422084432841

5. 源码分析

5.1. 标签解析

image-20210924073517589

  1. 启动容器的 main 方法执行时就会调用标签解析器
    Spring 解析配置文件的总接口:BeanDefinitionParser,其中 Dubbo 的实现类为 DubboBeanDefinitionParser,其中的方法为 parse()
    image-20200413112454357

  2. 在 DubboBeanDefinitionParser 创建时,有一个 DubboNameSpaceHandler 类的 init() 方法,会给每一个标签注册一个解析器,解析器中就绑定了 class 类别
    image-20200413114008915

  3. 在 DubboDefinitionParser 的 parse() 中就能根据标签找到对应的 bean 来封装配置文件中的信息。值得注意的是 service 标签对应的叫 ServiceBean,reference 对应的叫 ReferenceBean,其他的都叫 xxxConfig,为什么与众不同,因为有超能力,分别用于暴露服务和引用服务。

5.2. ServiceBean 版

https://blog.51cto.com/u_15281317/2942413

5.3. Dubbo 启动原理 -2.7.7- 注解版

https://juejin.cn/post/7024797126336970766#heading-6
Dubbo源码阅读四:在Spring下DubboBootstrap的启动过程

https://juejin.cn/post/6945839380799946766

5.3.1. 流程图

image.png

5.3.2. 属性配置解析器 -@EnableDubboConfig

当你使用 @EnableDubbo 注解启动 Dubbo 的时候,会加载它的 @EnableDubboConfig@DubboComponentScan 注解,分别用于处理 Dubbo 属性配置和解析 Dubbo 服务

1
2
3
4
5
6
7
8
9
10
11
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
// 处理配置文件中(例如yaml文件)的dubbo配置
@EnableDubboConfig
// 扫描并解析使用了dubbo注解的组件(例如@Service)
@DubboComponentScan
public @interface EnableDubbo {
......
}

@EnableDubboConfig@DubboComponentScan 注解使用了 Spring 的 @Import 注解来加载具体的实现类。

1
2
@Import(DubboConfigConfigurationRegistrar.class)
@Import(DubboComponentScanRegistrar.class)

5.3.2.1. DubboConfigConfigurationRegistrar

DubboConfigConfigurationRegistrar 用于将不同的属性加载到不同的配置文件中
registerBeans 方法最终通过 ConfigurationBeanBindingsRegister 将解析之后的配置类注册到 BeanDefinitionMap
image.png

image.png
最终在 Spring 容器中他们会被初始化成若干对象,例如:dubbo:registry 会转换成 org.apache.dubbo.config.RegistryConfig#0

5.3.3. 注解解析器 -@DubboComponentScan

5.3.3.1. DubboComponentScanRegistrar

image.png

image.png

DubboComponentScanRegistrar 主要是用来将 ServiceAnnotationBeanPostProcessor (2.7 以上为 ServiceClassPostProcessor)注册到 BeanDefinitionMap 中。
在 Spring 调用 BeanFactory 相关的后置处理器(invokeBeanFactoryPostProcessors)时,会使用 ServiceAnnotationBeanPostProcessor@DubboService 相关注解注册到 BeanDefinitionMap

image.png
ServiceClassPostProcessor 中,它注册了一个 dubbo 监听器,用于监听 Spring 容器的刷新、关闭事件,同时也将 @DubboService 注解的类注册到了 BeanDefinitionMap 中

image.png

5.3.4. 启动

https://juejin.cn/post/7024797126336970766#heading-7

5.3.4.1. ServiceClassPostProcessor

image.png

5.3.4.1.1. DubboBootstrapApplicationListener

伴随着 Spring 容器的启动,在 invokeBeanFactoryPostProcessors 阶段我们注册了 dubbo 相关的组件到 IOC,在 finishBeanFactoryInitialization(beanFactory) Dubbo 的组件被初始化、实例化,最后 Dubbo 通过监听 Spring 事件的方式完成启动器的调用、服务导出等操作
DubboBootstrap 的启动是通过监听 Spring 事件实现的。Spring 会在容器 Refresh 的最后一步发送一个事件 ContextRefreshedEvent,表示容器刷新完毕。

image.png

对于 ContextRefreshedEvent 事件的监听,最终调用了 dubboBootstrap.start() 方法,在这个方法里,Dubbo 完成了对服务的导出(暴露),导出服务的过程中,用到了 DUBBO SPI 机制

5.3.4.1.2. ServiceConfig
5.3.4.1.3. ServiceNameMappingListener

ServiceNameMapping 及其子类承接了对注册中心的调用,以 nacos 为例调用逻辑如下图
image.png

5.4. 服务暴露与引用原理 -2.7.7- 注解版

https://juejin.cn/post/6874731589243240461

5.5. 服务暴露原理 -xml 版⭐️🔴

%%
▶1.🏡⭐️◼️【🌈费曼无敌🌈⭐️第一步⭐️】◼️⭐️-point-20230328-0629%%
❕ ^gl53ku

5.5.1. 封装 serviceBean

IOC 容器启动时,BeanDefinitionParser 的实现类 DubboBeanDefinitionParser,会解析 dubbo 的标签,而在解析 service 标签时,会将 service 的 Beandefinition 封装成 serviceBean

image-20200413162808979

5.5.2. 实现了 【InitializingBean】

image-20210924073843703

5.5.2.1. afterpropertiesSet- 保存信息到 serviceBean

在组件实例化完成,属性设置完之后,调用 afterpropertiesSet() 方法,把配置中的 provider、application、module、registries、monitor、protocols 等的信息保存起来,保存到当前的 serviceBean 里面,最后组装成 URL

image-20210924080634932

image-20210924080658454

5.5.3. 实现了【 ApplicationListener】

image-20210924074228912

📢 : 2.7 版本注解版变动挺大,没有实现 ApplicationListener
image.png

5.5.3.1. onApplicationEvent- 加载注册中心的信息

IOC 容器刷新完成,所有对象都创建完成之后,回调 onApplicationEvent(),执行 export、doExport、doExportUrls 等方法,其中会根据不同的协议暴露不同的服务

image-20210924080346850

image-20210924080923918

image-20210924080952304

image-20210924081059881

5.5.3.2. 将目标服务实现类、接口、URL 等信息封装成 invoker 执行器

使用 proxyFactory 将目标服务实现类、接口、URL 等信息封装成 invoker 执行器
image-20210924081252224

5.5.3.3. protocol.export(warpperInvoker)- 暴露 invoker

image-20210924081305979

基于 Java 的 SPI 机制,会得到 2 个 protocol

image-20210924101256388

image-20210924092702515

https://www.bilibili.com/video/BV1AP4y1Y7YX?p=28

image-20211002202328113

5.5.3.3.1. RegistryProtocol.export- 暴露 Regisry

export 中有 2 个关键方法:

5.5.3.3.1.1. doLocalExport

invokeDelegete 暴露会进入 DubboProtocol.class 中的 export 方法
image-20211002203437343

image-20210924093034314

invokeDelegete 暴露会进入 DubboProtocol.class 中的 export 方法
image-20211002203919831

image-20211002203950166

image-20211002204038478

image-20210924093208709

接下面的 DubboProtocol.export

5.5.3.3.1.2. DubboProtocol.export- 暴露 Dubbo- 启动 netty 服务器

image-20210924093438794

image-20211002204347042

在 createServer 中启动 netty 服务器,监听 20882 端口

5.5.3.4. ProviderConsumerRegTable.registerProvider- 注册服务

image-20210924093845033

image-20210924081709274

image-20211002204723685

5.6. 服务引用原理 -xml 版⭐️🔴

%%
▶2.🏡⭐️◼️【🌈费曼无敌🌈⭐️第一步⭐️】◼️⭐️-point-20230328-0653%%
❕ ^3aliel

image-20210924084531649

5.6.1. 实现了 【FactoryBean】

image-20210924100429599

  1. 解析 reference 注解时,发现引用 UserService,就需要在容器中找,因为 ReferenceBean 实现了 FactoryBean,所以调用 getObject 方法

image-20210924100827788

image-20210924100802949

image-20211003070252288

  1. 在 get() 方法中调用了创建代理的 createProxy() 方法,在这个方法里有用到了通过 SPI 方式加载的 Protocol 类
    map 中存放的是 reference 标签中的属性,用来创建代理类,然后调用 refer,doRefer,subscribe 等关键方法

image-20211003070217503

  1. 与暴露方法同样的,引用方法也有 2 个实现,分别是 DubboProtocol 和 RegistryProtocol 中的 refer() 方法

urls 是注册中心的地址
image-20211003070628762

image-20210924101141683

5.6.2. RegistryProtocol.refer

  1. 先调用 RegistryProtocol 中的 refer() 方法,在该方法里到注册中心订阅服务,获取服务信息。

image-20210924110432057

image-20210924110754573

image-20210924110832104

subscribe 方法会调用 dubboProtocol.refer

5.6.3. DubboProtocol.refer

  1. 在订阅服务的方法中调用了 DubboProtocol 的 refer() 方法,方法中 getClients() 方法中创建客户端。
    getClients()initClient()

image-20210924084254521

Exchanges.connect(url,xxx)

  1. 创建 netty 的客户端,然后将带有连接属性的 invoker 返回
  2. 最后,将 invoker 注册到 ProviderConsumerRegTable 中,注册地址为订阅地址

image-20210924111102833

image-20210924111128709

5.7. 服务调用原理 -xml 版

image-20210924085205572

image-20210924111722548

5.7.1. 进入 InvokerInvocationHandler

  1. 代理对象层层封装了 Invoker 对象,里面是一些真正要执行的方法
    image-20210924111756013

5.7.2. 执行 new RpcInvocation 方法

  1. 可以在刚开始给代理对象加一些 filter,比如 cache、mock(比如服务降级) 功能
  2. 其中 InvokerInvocationHanlder 中的 invoke() 方法先到 MockClusterInvoker 类中,这个类可以封装多个 Invoker 对象,比如 failover、failback 功能的 Invoker
    首先会进入 MockClusterInvoker 中,获取失败容错信息

image-20211003072302993

5.7.3. 获取负载均衡机制

  1. 多个 Invoker 可以经过通过 SPI 获取到的负载均衡机制进行负载均衡
    在注册中心找想要执行的方法列表,如果是多个则获取负载均衡机制,然后执行 doInvoker

image-20211003071436483

image-20210924111843322

  1. 最终底层真正执行方法的 Invoker 是 DubboInvoker,通过 client 调用底层 Netty 客户端进行交互
    doInvoke 方法进入 FailoverClusterInvoker 中,随机选择一个 Invoker,然后执行 invoker.invoke

image-20211003072550600

5.7.4. 经过多个 filter

invoker 是封装的 filter,会经过多个 filter

image-20211003084214043

image-20211003082843646

5.7.5. DubboInvoker 方法中的 doInvoke 方法

经过多个 filter 之后,是 DubboInvoker 方法中的 doInvoke 方法,真正执行功能的方法

image-20210924112129947

5.7.6. currentClient.request

image-20211003084855353

6. 分层设计

image-20211003102405068
image-20211003102603060

7. 协议

image-20200422012747019
image-20200422012807396

8. 与 SpringBoot 整合

SpringBoot 与 dubbo 整合的三种方式:

1)、导入 dubbo-starter,在 application.properties 配置属性,使用@Service【暴露服务】使用@Reference【引用服务】

2)、保留 dubbo xml 配置文件; 导入 dubbo-starter,使用@ImportResource 导入 dubbo 的配置文件即可

3)、使用注解 API 的方式:将每一个组件手动创建到容器中,让 dubbo 来扫描其他的组件

image-20200422000348930

image-20200422000305379

9. Dubbo SPI 与 JDK SPI 区别

https://juejin.cn/post/6872138926216511501

10. 面试题

10.1. 与 SpringCloud 的区别

分布式专题-16、SpringCloud

10.2. 动态感知服务下线

https://www.bilibili.com/video/BV1Z44y1372E/?spm_id_from=333.788&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

10.3. 容错机制

https://www.bilibili.com/video/BV1hP4y1u77c/?spm_id_from=333.788&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

10.4. 对 Dubbo 的理解

https://www.bilibili.com/video/BV1FU4y1K7C7/?spm_id_from=333.788&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

10.5. 文字面试题

[[Dubbo面试题 47道.pdf]]

11. 参考与感谢

11.1. Dubbo 教程 _ 雷丰阳 _ 尚硅谷

11.1.1. 视频

https://www.bilibili.com/video/BV1Gb411T7Ha?p=12

11.1.2. 资料

注解版

1
/Volumes/Seagate Bas/001-study/语言工具/Dubbo/课件、资料/课件

11.2. 黑马程序员

11.2.1. 视频

https://www.bilibili.com/video/BV1VE411q7dX?p=8&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

11.2.2. 资料

非注解版:Spring、SpringMVC 整合

1
/Users/Enterprise/0003-Architecture/005-分布式专题/2、分布式开发框架Dubbo/资料--分布式开发框架Dubbo

11.3. 微服务 dubbo 和 springcloud 如何抉择?

https://www.bilibili.com/video/BV1rJ411t7MF?p=5

11.4. 其他

1
/Users/taylor/Nutstore Files/Obsidian_data/pages/002-schdule/001-Arch/001-Subject/013-DemoCode/JavaYouth/docs/dubbo-sourcecode-v1

11.5. 技术文章摘抄

11.5.1. 视频

https://www.bilibili.com/video/BV1Bc411V7AR?p=19&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

11.5.2. 文档

看服务暴露和服务引用的全流程

1
/Volumes/Seagate Bas/001-ArchitectureRoad/000-极客时间/31-从0开始学微服务/03-模块二 落地微服务 (14讲)/14丨开源RPC框架如何选型?.pdf

11.6. 网络笔记

[[Dubbo基础及原理机制 - 胡小华 - 博客园]]