Dubbo的基础知识
Dubbo的核心架构是怎样的?
-
Registry:注册中心。 负责服务地址的注册与查找,服务的 Provider 和 Consumer 只在启动时与注册中心交互。注册中心通过长连接感知 Provider 的存在,在 Provider 出现宕机的时候,注册中心会立即推送相关事件通知 Consumer;
-
Provider:服务提供者。 在它启动的时候,会向 Registry 进行注册操作,将自己服务的地址和相关配置信息封装成 URL 添加到 ZooKeeper 中;
-
Consumer
:服务消费者。 在它启动的时候,会向 Registry 进行订阅操作。订阅操作会从 ZooKeeper 中获取 Provider 注册的 URL,并在 ZooKeeper 中添加相应的监听器。
- 获取到 Provider URL 之后,Consumer 会根据负载均衡算法从多个 Provider 中选择一个 Provider 并与其建立连接,最后发起对 Provider 的 RPC 调用;
- 如果 Provider URL 发生变更,Consumer 将会通过之前订阅过程中在注册中心添加的监听器,获取到最新的 Provider URL 信息,进行相应的调整,比如断开与宕机 Provider 的连接,并与新的 Provider 建立连接;
- Consumer 与 Provider 建立的是长连接,且 Consumer 会缓存 Provider 信息,所以一旦连接建立,即使注册中心宕机,也不会影响已运行的 Provider 和 Consumer;
-
Monitor:监控中心。 用于统计服务的调用次数和调用时间。Provider 和 Consumer 在运行过程中,会在内存中统计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。监控中心在上面的架构图中并不是必要角色,监控中心宕机不会影响 Provider、Consumer 以及 Registry 的功能,只会丢失监控数据而已。
为什么说Dubbo是基于URL驱动的?
- URL 在 Dubbo 中被当作是“公共的契约”。一个 URL 可以包含非常多的扩展点参数,URL 作为上下文信息贯穿整个扩展点设计体系;
- Dubbo 基于 URL驱动的好处:
- 统一数据格式规范,让交互变得简单化;
- 使用URL作为方法参数,便于参数扩展,新参数只需要以k/v形式追加到URL即可,不需要改变入参或返回值的数据结构;
Dubbo的URL由哪些部分组成?
- 下面是Provider 注册到 ZooKeeper 上的 URL 例子:
dubbo://172.17.32.91:20880/org.apache.dubbo.demo.DemoService ?anyhost=true&application=dubbo-demo-api-provider&dubbo=2.0.2 &interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=32508&release= &side=provider×tamp=1593253404714dubbo://172.17.32.91:20880/org.apache.dubbo.demo.DemoService ?anyhost=true&application=dubbo-demo-api-provider&dubbo=2.0.2 &interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync &pid=32508&release=&side=provider×tamp=1593253404714
- protocol:dubbo 协议;
- host/port:172.17.32.91:20880;
- path:org.apache.dubbo.demo.DemoService;
- parameters:参数键值对,这里是问号后面的参数。
相较于JDK SPI而言,Dubbo SPI做了哪些改进?
- JDK SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源,而Dubbo SPI可以按需进行加载,实例化后会进行缓存;
- Dubbo按照SPI配置文件的用途,将其分为了三个目录:
- META-INF/services/:该目录下的 SPI 配置文件用来兼容 JDK SPI ;
- META-INF/dubbo/:该目录用于存放用户自定义 SPI 配置文件;
- META-INF/dubbo/internal/:该目录用于存放 Dubbo 内部使用的 SPI 配置文件。
- Dubbo 将 SPI 配置文件改成了 KV 格式,key被称为扩展名,我们可以指定扩展名来查找具体的实现,从而实现按需加载;
- 扩展名更利于我们排查异常,Dubbo SPI在抛出异常时,会携带扩展名信息,而不是简单提升具体实现类无法加载,提升我们排查问题的效率。
Dubbo的扩展点有哪些分类?
- 普通扩展类:最基础的,配置在SPI配置文件中的扩展类实现;
- 包装扩展类:Wrapper类是一种装饰者模式,在构造方法中传入一个具体扩展接口的实现,属于Dubbo的自动包装特性。在ExtensionLoader在加载扩展时,如果发现这个扩展类包含其它扩展点作为构造函数参数,则这个扩展类就会被认定为是Wrapper类;
- 自适应扩展类:一个扩展接口会有多种实现类,具体使用哪个实现类可以不写死在配置或代码中,在运行时,通过传入URL中的某些参数动态确定,这属于扩展点的自适应特性。
Dubbo的@Adaptive注解与@Activate注解的区别是什么?
- @Adaptive称为自适应扩展点注解。自适应扩展指的是,一个扩展接口往往会有多种实现类,因为Dubbo是基于URL驱动,所以在运行时,通过传入URL中的某些参数来动态控制具体实现;
- @Adaptive可以修饰类级别与方法级别:
- 修饰方法级别时,Dubbo初始化扩展点时会自动生成和编译一个动态的Adaptive类,是一种动态代理的模式,方法里会有一些抽象的通用逻辑,根据解析URL得到的信息,找到并调用真正的实现类;
- 修饰类级别时,省略了生成动态代理类的过程,由该类中决定具体实现,可理解为是一种静态代理的模式。另外对于同一个扩展点,类级别的Adaptive只能有一个。
- @Adaptive可以修饰类级别与方法级别:
- @Activate称为自动激活扩展点注解。主要使用在有多个扩展点实现、需要同时根据不同条件被激活的场景中,如Filter需要多个同时激活,因为每个Filter实现的是不同的功能;
- @Activate的参数:
参数名 | 效果 |
---|---|
String[] group() | URL中的分组如果匹配则激活 |
String[] value() | URL中如果包含该key值,则会激活 |
String[] before() | 填写扩展点列表,表示哪些扩展点要在本扩展点之前激活 |
String[] after() | 表示哪些扩展点需要在本扩展点之后激活 |
int order() | 排序信息 |
服务通信
如何设计一个rpc框架?
- 主要的模块:
- protocol:简易版 RPC 框架的自定义协议;
- serialization:提供了自定义协议对应的序列化、反序列化的相关工具类;
- codec:提供了自定义协议对应的编码器和解码器;
- transport:基于 Netty 提供了底层网络通信的功能,其中会使用到 codec 包中定义编码器和解码器,以及 serialization 包中的序列化器和反序列化器;
- registry:基于 ZooKeeper 和 Curator 实现了简易版本的注册中心功能;
- proxy:使用 JDK 动态代理实现了一层代理。
一次rpc请求的流程是怎样的?
- Client 首先会调用本地的代理,也就是图中的 Proxy;
- Client 端 Proxy 会按照协议(Protocol),将调用中传入的数据序列化成字节流;
- 之后 Client 会通过网络,将字节数据发送到 Server 端;
- Server 端接收到字节数据之后,会按照协议进行反序列化,得到相应的请求信息;
- Server 端 Proxy 会根据序列化后的请求信息,调用相应的业务逻辑;
- Server 端业务逻辑的返回值,也会按照上述逻辑返回给 Client 端。
序列化的意义是什么?
- 简单来说,序列化是把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程称为对象的序列化。反序列化是序列化的逆向过程,把字节数组反序列化为对象。
Dubbo服务发布的流程是怎样的?
- dubbo的service本质是一个被Spring管理的ServiceBean,ServiceBean实现了众多Spring提供的接口;
- afterPropertiesSet:解析配置;
- onApplicationEvent:执行发布流程;
- 服务发布流程:
- 基于Spring进行配置解析,将配置属性转换成ServiceBean对象;
- 各种判断校验逻辑,保证信息的安全;
- 组装URL(registry://、dubbo://、injvm://);
- 构建invoker对象;
- RegistryProtocol.export(向注册中心注册URL);
- 各种wrapper的增强(gos、listener、filter链);
- DubboProtocol.export(发布服务);
- 启动一个NettyServer。
Dubbo服务引用的流程是怎样的?
- Dubbo 的 consumer 会通过 ReferenceBean 实现服务引用;
- Dubbo 服务引用的时机有两个:
- 第一个是在 Spring 容器调用 ReferenceBean 的 afterPropertiesSet 方法时引用服务;
- 第二个是在 ReferenceBean 对应的服务被注入到其他类中时引用,而入口都是getObject方法。
- 服务引用流程:
- 生成远程服务的代理;
- 通过注册中心获得目标服务的url地址;
- 实现远程网络通信;
- 实现负载均衡;
- 实现集群容错。
Dubbo的Invoker是什么?
- Invoker 是Dubbo 的核心模型,其它模型都向它靠拢,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现;
- 在服务提供方,Invoker 用于调用服务提供类。在服务消费方,Invoker 用于执行远程调用。Invoker 是由 Protocol 实现类构建而来。
在Dubbo中Proxy 和 Wrapper的作用是什么?
- Consumer 端的 Proxy 底层屏蔽了复杂的网络交互、集群策略以及 Dubbo 内部的 Invoker 等概念,提供给上层使用的是业务接口;
- Provider 端的 Wrapper 是将个性化的业务接口实现,统一转换成 Dubbo 内部的 Invoker 接口实现;
- 正是由于 Proxy 和 Wrapper 这两个组件的存在,Dubbo 才能实现内部接口和业务接口的无缝转换。
集群
Dubbo的Directory(服务目录)是什么?
- Directory中存储了一些和服务提供者有关的信息,通过Directory,服务消费者可获取到服务提供者的信息,比如 ip、端口、服务协议等。通过这些信息,服务消费者就可通过 Netty 等客户端进行远程调用。
- 一个服务集群中,provider的数量是会动态变更的,Directory从注册中心获取provider的配置信息后,会为每条配置生成对应的Invoker对象,因此Directory 可以看成是一组Invoker 的集合,它会随着注册中心的变化而动态调整。
Dubbo Cluster的作用是什么?
- 在Dubbo体系中,集群模块是服务提供者和服务消费者的中间层,为服务消费者屏蔽了服务提供者的情况,这样服务消费者就可以专心处理远程调用相关事宜。比如发请求,接受服务提供者返回的数据等;
- 集群 Cluster 用途是将多个服务提供者合并为一个 Cluster Invoker,并将这个 Invoker 暴露给服务消费者。这样一来,服务消费者只需通过这个 Invoker 进行远程调用即可,至于具体调用哪个服务提供者,以及调用失败后如何处理等问题,现在都交给集群模块去处理。
Dubbo Cluster的工作流程是怎样的?
- 第一个阶段:服务消费者初始化期间,集群 Cluster 实现类为服务消费者创建 Cluster Invoker 实例;
- 第二个阶段:
- 服务消费者进行远程调用时,Cluster Invoker 首先会调用 Directory 的 list 方法列举 Invoker 列表;
- 然后调用 Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker(例如黑名单过滤);
- 当Cluster Invoker 拿到 Directory 返回的 Invoker 列表后,它会通过 LoadBalance 从 Invoker 列表中选择一个 Invoker,通过这个Invoker实例进行远程调用。
Dubbo有哪些集群容错策略?
名称 | 简介 |
---|---|
Failover | 失败自动切换:当出现请求失败时,会重试其它服务器。可以通过retries设置重试次数,默认为2次。该方式是dubbo默认的容错机制,适用于读操作或幂等的写操作。 |
Failfast | 快速失败:当请求失败后,快速返回异常结果,不做任何重试。适用于非幂等接口。 |
Failsafe | 失败安全:当请求出现异常时,直接忽略异常。适用于佛系调用场景,即不关心调用是否成功,也不想影响外层的调用,例如不重要的日志同步等。 |
Failback | 失败自动恢复:请求失败后,会自动记录在失败队列中,并由一个定时线程池定时重试。适用于一些异步请求或最终一致性的请求。 |
Forking | 并行调用:同时调用多个相同的服务,只要有一个返回,则立即返回结果,可通过forks设置并行数。适用于某些对实时性要求极高的调用上,但也会浪费更多的资源。 |
Broadcast | 广播调用:广播调用所有可用的服务,任意一个节点报错则报错。适用于服务测试。 |
Available | 最简单的调用:请求不会做负载均衡,遍历所有服务列表,找到第一个可用的节点,直接请求并返回,如果没有可用节点则抛出异常。 |
Dubbo有哪些负载均衡策略?
算法名称 | 说明 |
---|---|
Random LoadBalance | 随机可以按权重设置随机概率,调用量越大分布月均匀 |
RoundRobin LoadBalance | 轮询可以根据权重设置轮询比例。 |
LeastActive LoadBalance | 最少活跃调用数根据活跃度进行分配调用,使慢的提供者收到更少的请求,如果活跃数相同则随机调用,活跃数是指调用前后的计数差。 |
ConsistentHash LoadBalance | 一致性Hash相同参数的请求总是发到同一提供者,当某台提供者挂掉时,基于虚拟节点,相应的请求会平摊到其它提供者,不会引起剧烈变动。 |
限流、熔断、降级
Dubbo是如何实现限流的?
- 通过Dubbo Service注解的executes属性配置最大并行数;
- 限流的具体逻辑由ExecuteLimitFilter实现,本质是基于信号量控制并发数;
Dubbo中如何实现熔断?
- Dubbo没有提供自动熔断策略;
Dubbo是否支持降级?
- 可以通过Reference注解中的mock属性返回默认值。
性能调优
如何根据参数对Dubbo进行调优?
- Schema 配置参考手册 | Apache Dubbo