分布式专题-13、Dubbo
1. Dubbo
1.1. 是什么
Dubbo 是一款高性能、轻量级的开源 RPC 框架。由 10 层模式构成,整个分层依赖由上至下。通过这张图我们也可以将 Dubbo 理解为三层模式:
第一层的 Business 业务逻辑层由我们自己来提供接口和实现还有一些配置信息。
第二层的 RPC 调用的核心层负责封装和实现整个 RPC 的调用过程、负载均衡、集群容错、代理等核心功能。
第三层的 Remoting 则是对网络传输协议和数据转换的封装。
1.2. 核心能力 (特性)
1.3. 架构
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. 工作原理
- 服务启动的时候,provider 和 consumer 根据配置信息,连接到注册中心 register,分别向注册中心注册和订阅服务
- register 根据服务订阅关系,返回 provider 信息到 consumer,同时 consumer 会把 provider 信息缓存到本地。如果信息有变更,consumer 会收到来自 register 的推送
- consumer 生成代理对象,同时根据负载均衡策略,选择一台 provider ,同时定时向 monitor 记录接口的调用次数和时间信息
- consumer 拿到代理对象之后,通过代理对象发起接口调用
- provider 收到请求后对数据进行反序列化,然后通过代理调用具体的接口实现
2.1. 负载均衡⭐️🔴
2.2. 超时与重试
默认使用 consumer 中的 timeout 为 1000ms
该数字不包含刚开始的 1 次
2.2.1. 配置优先级
2.3. 服务容错⭐️🔴
https://www.bilibili.com/video/BV1t7411j7P7?p=15
2.4. 服务降级
2.5. 延迟暴露
2.6. 线程模型
3. 注册中心
3.1. 配置方式
5. 源码分析
5.1. 标签解析
启动容器的 main 方法执行时就会调用标签解析器
Spring 解析配置文件的总接口:BeanDefinitionParser,其中 Dubbo 的实现类为 DubboBeanDefinitionParser,其中的方法为 parse()在 DubboBeanDefinitionParser 创建时,有一个
DubboNameSpaceHandler
类的 init() 方法,会给每一个标签注册一个解析器,解析器中就绑定了 class 类别在 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. 流程图
5.3.2. 属性配置解析器 -@EnableDubboConfig
当你使用 @EnableDubbo
注解启动 Dubbo 的时候,会加载它的 @EnableDubboConfig
和 @DubboComponentScan
注解,分别用于处理 Dubbo 属性配置和解析 Dubbo 服务
1 |
|
@EnableDubboConfig
和 @DubboComponentScan
注解使用了 Spring 的 @Import
注解来加载具体的实现类。
1 |
|
5.3.2.1. DubboConfigConfigurationRegistrar
DubboConfigConfigurationRegistrar
用于将不同的属性加载到不同的配置文件中registerBeans
方法最终通过 ConfigurationBeanBindingsRegister
将解析之后的配置类注册到 BeanDefinitionMap
最终在 Spring 容器中他们会被初始化成若干对象,例如:dubbo:registry
会转换成 org.apache.dubbo.config.RegistryConfig#0
5.3.3. 注解解析器 -@DubboComponentScan
5.3.3.1. DubboComponentScanRegistrar
DubboComponentScanRegistrar
主要是用来将 ServiceAnnotationBeanPostProcessor
(2.7 以上为 ServiceClassPostProcessor
)注册到 BeanDefinitionMap
中。
在 Spring 调用 BeanFactory 相关的后置处理器(invokeBeanFactoryPostProcessors
)时,会使用 ServiceAnnotationBeanPostProcessor
将 @DubboService
相关注解注册到 BeanDefinitionMap
在 ServiceClassPostProcessor
中,它注册了一个 dubbo 监听器,用于监听 Spring 容器的刷新、关闭事件,同时也将 @DubboService
注解的类注册到了 BeanDefinitionMap
中
5.3.4. 启动
https://juejin.cn/post/7024797126336970766#heading-7
5.3.4.1. ServiceClassPostProcessor
5.3.4.1.1. DubboBootstrapApplicationListener
伴随着 Spring 容器的启动,在 invokeBeanFactoryPostProcessors 阶段我们注册了 dubbo 相关的组件到 IOC,在 finishBeanFactoryInitialization(beanFactory) Dubbo 的组件被初始化、实例化,最后 Dubbo 通过监听 Spring 事件的方式完成启动器的调用、服务导出等操作DubboBootstrap
的启动是通过监听 Spring 事件实现的。Spring 会在容器 Refresh 的最后一步发送一个事件 ContextRefreshedEvent
,表示容器刷新完毕。
对于 ContextRefreshedEvent
事件的监听,最终调用了 dubboBootstrap.start() 方法,在这个方法里,Dubbo 完成了对服务的导出(暴露),导出服务的过程中,用到了 DUBBO SPI 机制
5.3.4.1.2. ServiceConfig
5.3.4.1.3. ServiceNameMappingListener
ServiceNameMapping 及其子类承接了对注册中心的调用,以 nacos 为例调用逻辑如下图
5.4. 服务暴露与引用原理 -2.7.7- 注解版
https://juejin.cn/post/6874731589243240461
5.5. 服务暴露原理 -xml 版⭐️🔴
❕ ^gl53ku
5.5.1. 封装 serviceBean
IOC 容器启动时,BeanDefinitionParser 的实现类 DubboBeanDefinitionParser,会解析 dubbo 的标签,而在解析 service 标签时,会将 service 的 Beandefinition 封装成 serviceBean
5.5.2. 实现了 【InitializingBean】
5.5.2.1. afterpropertiesSet- 保存信息到 serviceBean
在组件实例化完成,属性设置完之后,调用 afterpropertiesSet()
方法,把配置中的 provider、application、module、registries、monitor、protocols 等的信息保存起来,保存到当前的 serviceBean 里面,最后组装成 URL
5.5.3. 实现了【 ApplicationListener】
📢 : 2.7 版本注解版变动挺大,没有实现 ApplicationListener
5.5.3.1. onApplicationEvent- 加载注册中心的信息
IOC 容器刷新完成,所有对象都创建完成之后,回调 onApplicationEvent(),执行 export、doExport、doExportUrls 等方法,其中会根据不同的协议暴露不同的服务
5.5.3.2. 将目标服务实现类、接口、URL 等信息封装成 invoker 执行器
使用 proxyFactory 将目标服务实现类、接口、URL 等信息封装成 invoker 执行器
5.5.3.3. protocol.export(warpperInvoker)- 暴露 invoker
基于 Java 的 SPI 机制,会得到 2 个 protocol
https://www.bilibili.com/video/BV1AP4y1Y7YX?p=28
5.5.3.3.1. RegistryProtocol.export- 暴露 Regisry
export 中有 2 个关键方法:
5.5.3.3.1.1. doLocalExport
invokeDelegete 暴露会进入 DubboProtocol.class 中的 export 方法
invokeDelegete 暴露会进入 DubboProtocol.class 中的 export 方法
接下面的 DubboProtocol.export
5.5.3.3.1.2. DubboProtocol.export- 暴露 Dubbo- 启动 netty 服务器
在 createServer 中启动 netty 服务器,监听 20882 端口
5.5.3.4. ProviderConsumerRegTable.registerProvider- 注册服务
5.6. 服务引用原理 -xml 版⭐️🔴
❕ ^3aliel
5.6.1. 实现了 【FactoryBean】
- 解析 reference 注解时,发现引用 UserService,就需要在容器中找,因为 ReferenceBean 实现了 FactoryBean,所以调用 getObject 方法
- 在 get() 方法中调用了创建代理的
createProxy()
方法,在这个方法里有用到了通过 SPI 方式加载的 Protocol 类
map 中存放的是 reference 标签中的属性,用来创建代理类,然后调用 refer,doRefer,subscribe 等关键方法
- 与暴露方法同样的,引用方法也有 2 个实现,分别是 DubboProtocol 和 RegistryProtocol 中的 refer() 方法
urls
是注册中心的地址
5.6.2. RegistryProtocol.refer
- 先调用 RegistryProtocol 中的 refer() 方法,在该方法里到注册中心订阅服务,获取服务信息。
subscribe 方法会调用 dubboProtocol.refer
5.6.3. DubboProtocol.refer
- 在订阅服务的方法中调用了 DubboProtocol 的 refer() 方法,方法中 getClients() 方法中创建客户端。
getClients() → initClient()
Exchanges.connect(url,xxx)
- 创建 netty 的客户端,然后将带有连接属性的 invoker 返回
- 最后,将 invoker 注册到 ProviderConsumerRegTable 中,注册地址为订阅地址
5.7. 服务调用原理 -xml 版
5.7.1. 进入 InvokerInvocationHandler
- 代理对象层层封装了 Invoker 对象,里面是一些真正要执行的方法
5.7.2. 执行 new RpcInvocation 方法
- 可以在刚开始给代理对象加一些 filter,比如 cache、mock(比如服务降级) 功能
- 其中 InvokerInvocationHanlder 中的 invoke() 方法先到 MockClusterInvoker 类中,这个类可以封装多个 Invoker 对象,比如 failover、failback 功能的 Invoker
首先会进入 MockClusterInvoker 中,获取失败容错信息
5.7.3. 获取负载均衡机制
- 多个 Invoker 可以经过通过 SPI 获取到的负载均衡机制进行负载均衡
在注册中心找想要执行的方法列表,如果是多个则获取负载均衡机制,然后执行 doInvoker
- 最终底层真正执行方法的 Invoker 是 DubboInvoker,通过 client 调用底层 Netty 客户端进行交互
doInvoke 方法进入 FailoverClusterInvoker 中,随机选择一个 Invoker,然后执行 invoker.invoke
5.7.4. 经过多个 filter
invoker 是封装的 filter,会经过多个 filter
5.7.5. DubboInvoker 方法中的 doInvoke 方法
经过多个 filter 之后,是 DubboInvoker 方法中的 doInvoke 方法,真正执行功能的方法
5.7.6. currentClient.request
6. 分层设计
7. 协议
8. 与 SpringBoot 整合
SpringBoot 与 dubbo 整合的三种方式:
1)、导入 dubbo-starter,在 application.properties 配置属性,使用@Service【暴露服务】使用@Reference【引用服务】
2)、保留 dubbo xml 配置文件; 导入 dubbo-starter,使用@ImportResource 导入 dubbo 的配置文件即可
3)、使用注解 API 的方式:将每一个组件手动创建到容器中,让 dubbo 来扫描其他的组件
9. Dubbo SPI 与 JDK SPI 区别
https://juejin.cn/post/6872138926216511501
10. 面试题
10.1. 与 SpringCloud 的区别
分布式专题-16、SpringCloud10.2. 动态感知服务下线
10.3. 容错机制
10.4. 对 Dubbo 的理解
10.5. 文字面试题
[[Dubbo面试题 47道.pdf]]
11. 参考与感谢
11.1. Dubbo 教程 _ 雷丰阳 _ 尚硅谷
11.1.1. 视频
https://www.bilibili.com/video/BV1Gb411T7Ha?p=12
11.1.2. 资料
注解版
1 |
|
11.2. 黑马程序员
11.2.1. 视频
https://www.bilibili.com/video/BV1VE411q7dX?p=8&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
11.2.2. 资料
非注解版:Spring、SpringMVC 整合
1 |
|
11.3. 微服务 dubbo 和 springcloud 如何抉择?
https://www.bilibili.com/video/BV1rJ411t7MF?p=5
11.4. 其他
1 |
|
11.5. 技术文章摘抄
11.5.1. 视频
https://www.bilibili.com/video/BV1Bc411V7AR?p=19&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
11.5.2. 文档
1 |
|
11.6. 网络笔记
[[Dubbo基础及原理机制 - 胡小华 - 博客园]]