Dubbo 综合问题

什么是 Dubbo?

Apache Dubbo 是一款分布式、高性能、透明化的 RPC 服务开发框架,提供服务自动注册、自动发现等高效服务治理方案,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。

Dubbo 支持基于 IDL 或语言特定方式的服务定义,提供多种形式的服务调用形式(如同步、异步、流式等)。

Dubbo 帮助解决微服务组件之间的通信问题,提供了基于 HTTP、HTTP/2、TCP 等的多种高性能通信协议实现,并支持序列化协议扩展,在实现上解决网络连接管理、数据传输等基础问题。

Dubbo 官方提供的服务发现、动态配置、负载均衡、流量路由等基础组件可以很好的帮助解决微服务基础实践的问题。除此之外,您还可以用 Admin 控制台监控微服务状态,通过周边生态完成限流降级、数据一致性、链路追踪等能力。

Dubbo 服务可以直接部署在容器、Kubernetes、Service Mesh等多种架构下。

Dubbo 的核心功能?

Dubbo 主要有 3 个核心功能:

  • Remoting:网络通信框架,提供对多种 NIO 框架抽象封装,包括“同步转异步”和“请求-响应”模式的信息交换方式。
  • Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
  • Registry:服务注册,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

Dubbo 的核心组件?

Registry:注册中心,提供服务注册与服务发现

Container:服务运行容器

Monitor:监控中心,统计服务的调用次数和调用时间等

Provider:服务提供方

Consumer:服务消费方

Dubbo 的容器 Container?

Container 是一个独立的容器,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。

服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。

Dubbo 内置了哪几种服务容器:

  1. SpringContainer(默认)
  2. JettyContainer
  3. Log4jContainer
  4. JavaConfigContainer
  5. LogbackContainer

Dubbo 的核心配置?

dubbo.service服务配置
dubbo.reference引用配置
dubbo.protocol协议配置
dubbo.application应用配置
dubbo.module模块配置
dubbo.registry注册中心配置
dubbo.monitor监控中心配置
dubbo.provider提供者配置
dubbo.consumer消费者配置
dubbo.method方法配置
dubbo.argument参数配置

在 Provide 端可以配置的 Consumer 端的属性有:

  • timeout:调用超时时间
  • retries:失败重试次数,默认2次
  • loadbalance:负载均衡策略,默认随机
  • actives:消费者端最大并发调用限制

Dubbo 启动时如果依赖的服务不可用会怎样?

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,默认 check="true",可以通过 check="false" 关闭检查。

Dubbo 服务注册与发现的流程?

  1. Provider(提供者)绑定指定端口并启动服务
  2. 提供者连接注册中心,并发本机 IP、端口、应用信息和提供服务信息发送至注册中心存储
  3. Consumer(消费者)连接注册中心,并发送应用信息、所求服务信息至注册中心
  4. 注册中心根据消费者所求服务信息匹配对应的提供者列表发送至 Consumer 应用缓存。
  5. Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。
  6. Provider 状态变更会实时通知注册中心、在由注册中心实时推送至 Consumer

Dubbo 的架构设计?

Dubbo 框架设计一共划分了10个层,而最上面的 Service 层是留给实际想要使用 Dubbo 开发分布式服务的开发者实现业务逻辑的接口层。图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。

  • 服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现。
  • 配置层(Config):对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心,可以直接 new 配置类,也可以通过 Spring 解析配置生成配置类。 
  • 服务代理层(Proxy):服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton,以 ServiceProxy 为中心,扩展接口为 ProxyFactory。 
  • 服务注册层(Registry):封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory、Registry 和 RegistryService。可能没有服务注册中心,此时服务提供方直接暴露服务。 
  • 集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster、Directory、Router 和 LoadBalance。
  • 将多个服务提供方组合为一个服务提供方,实现对服务消费方来透明,只需要与一个服务提供方进行交互。 
  • 监控层(Monitor):RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为MonitorFactory、Monitor 和 MonitorService。 
  • 远程调用层(Protocol):封将 RPC 调用,以 Invocation 和 Result 为中心,扩展接口为Protocol、Invoker 和 Exporter。
  • Protocol是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。
  • Invoker是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。 
  • 信息交换层(Exchange):封装请求响应模式,同步转异步,以 Request 和 Response 为中心,扩展接口为 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer。 
  • 网络传输层(Transport):抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel、Transporter、Client、Server 和 Codec。 
  • 数据序列化层(Serialize):可复用的一些工具,扩展接口为 Serialization、 ObjectInput、ObjectOutput 和 ThreadPool。

Dubbo 的服务调用流程?

客户端发起调用,实际调用的是代理类,代理类最终调用的是 Client (默认Netty),需要构造好协议头,然后将 Java 的对象序列化生成协议体,然后网络调用传输。服务端的 NettyServer 接到这个请求之后,分发给业务线程池,由业务线程调用具体的实现方法。

以下是详细的调用该过程:

Dubbo 超时时间怎样设置?

Dubbo 超时时间设置有两种方式:

  1. 服务提供者端设置超时时间,在 Dubbo 的用户文档中,推荐如果能在服务端多配置就尽量多配置,因为服务提供者比消费者更清楚自己提供的服务特性。
  2. 服务消费者端设置超时时间,如果在消费者端设置了超时时间,以消费者端为主,即优先级更高。因为服务调用方设置超时时间控制性更灵活。如果消费方超时,服务端线程不会定制,会产生警告。

Dubbo 支持哪些序列化方式?

默认使用 Hessian 序列化,还有 Duddo、FastJson、Java 自带序列化。

Dubbo 默认使用什么通信框架?

Dubbo 默认使用 NIO Netty 框架,还可以使用 Mina、Grizzly。

注册了多个同一样的服务,如何测试指定的某个服务?

可以配置环境点对点直连,绕过注册中心,将以服务接口为单位,忽略注册中心的提供者列表

Dubbo 支持服务多协议吗?

Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。

当一个服务接口有多种实现时怎么做?

当一个接口有多种实现时,可以用 group 属性来分组,服务提供方和消费方都指定同一个 group 即可。

服务上线怎么兼容旧版本?

可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用,这个和服务分组的概念有一点类似。

Dubbo 可以对结果进行缓存吗?

可以,Dubbo 提供了声明式缓存,用于加速热门数据的访问速度,以减少用户加缓存的工作量

Dubbo 服务之间的调用是阻塞的吗?

默认是同步等待结果阻塞的,支持异步调用。

Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。
 

Dubbo 支持服务降级吗?

Dubbo 2.2.0 以上版本支持,通过 mock 属性配置。

方式一:mock="true",这种方式需要在相同包下有类名 + Mock后缀的实现类,即 com.xxx.service包下有 DemoServiceMock 类。

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="true" />

方式二:这种方式需要指定 Mock 类的全路径。

<dubbo:reference id="demoService" 
    interface="com.xxx.service.DemoService" 
    mock="com.xxx.service.DemoServiceMock" />

方式三:mock="[fail|force]return|throw xxx"

  • fail 或 force 关键字可选,表示调用失败或不调用强制执行 mock 方法,如果不指定关键字默认为 fail
  • return 表示指定返回结果,throw 表示抛出指定异常
  • xxx 根据接口的返回类型解析,可以指定返回值或抛出自定义的异常
<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="return" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="return null" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="fail:return aaa" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="force:return true" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="fail:throw" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="force:throw java.lang.NullPointException" />

Dubbo 优雅停机?

Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果使用 kill -9 PID 等强制关闭指令是不会执行优雅停机的,只有通过 kill PID 时才会执行。

停机流程:

  • 服务提供方: 
    • 停止时,先标记为不接受新的请求,新请求过来时直接报错,让客户端重试其他机器;
    • 然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭;
  • 服务消费方:
    • 停止时,不在发起新的调用请求,所有新的调用在客户端即报错;
    • 然后,检测有没有请求的相应还没有返回,等待相应返回,除非超时,则强制关闭;

在 Dubbo中,优雅停机是默认开启的,停机等待时间为10000毫秒。可以通过配置dubbo.service.shutdown.wait 来修改等待时间。

dubbo.service.shutdown.wait=20000

如果 ShutdownHock 不能生效,可以自行调用 ProtocolConfig.destoryAll() 方法。

当使用 org.apache.dubbo.container.Main 这种容器方式来使用 Dubbo 时,也可以通过配置 dubbo.shutdown.hook 为 true 来开启优雅停机。

Dubbo 的失效踢出?

在 Dubbo 中,失效踢出机制的目标是及时识别出不可用的服务提供者,并将其从可用的服务列表中移除,以避免将请求发送到不可用的服务上,从而提升系统的稳定性和可用性。Dubbo 的失效踢出机制基于心跳检测和定时任务,通过周期性地检测服务提供者的状态来判断是否失效,并在失效情况下将其踢出。

(1)心跳检测机制

Dubbo 使用心跳检测机制来监测服务提供者的状态,心跳检测通过在服务提供者和消费者之间建立长连接,并定时发送心跳包来确认服务提供者的存活状态,当服务提供者无法响应心跳包时,Dubbo 会判断其为失效,并触发失效踢出操作。

Dubbo 的心跳检测机制包括以下几个步骤:

  1. 心跳发送:服务消费者定时向服务提供者发送心跳包,以维持连接和确认服务提供者的存活状态。心跳包中包含了消费者的身份信息和相关参数。
  2. 心跳响应:服务提供者接收到心跳包后,进行响应,表示其仍然存活。响应中包含了提供者的身份信息和状态。
  3. 心跳超时:如果服务消费者在规定时间内没有收到服务提供者的心跳响应,就会判断服务提供者失效。

(2)定时任务实现失效踢出

Dubbo 使用定时任务来实现失效踢出,定时任务会周期性地检测服务提供者的状态,并根据心跳检测的结果来判断其是否失效。当服务提供者被判定为失效时,Dubbo 会将其从可用的服务列表中移除,从而实现失效踢出的效果。

Dubbo 的定时任务包括以下几个步骤:

  1. 任务启动:Dubbo 在启动时会创建一个定时任务,周期性地执行失效踢出操作。任务的执行频率可以通过配置进行调整,一般为几秒或几分钟执行一次。
  2. 服务状态检测:定时任务触发时,Dubbo 会遍历已知的服务提供者列表,检测每个服务提供者的状态。
  3. 失效判断:对于每个服务提供者,Dubbo 会检查其心跳状态,并根据心跳超时的规则判断其是否失效。如果服务提供者被判定为失效,将进行相应的失效处理。
  4. 失效踢出:当服务提供者失效时,Dubbo 会将其从可用的服务列表中移除,确保后续的请求不会被发送到失效的服务上。

服务失效踢出基于 zookeeper 的临时节点原理。

(3)失效恢复

除了失效踢出,Dubbo 还支持失效恢复机制。当一个服务提供者被判定为失效后,Dubbo 会记录其失效次数,并在一定的时间范围内尝试重新恢复该服务提供者。如果在恢复的过程中服务提供者重新响应了心跳包,Dubbo 会将其重新加入可用的服务列表,使其能够对外提供服务。

失效恢复机制可以有效地应对服务提供者的临时故障或网络抖动等问题,提高系统的稳定性和可靠性。

(4)配置失效踢出机制

Dubbo 提供了一系列的配置参数来调整失效踢出机制的行为,常用的配置参数包括:

  • 心跳超时时间:用于指定心跳检测的超时时间,即在该时间内没有收到心跳响应则认为服务提供者失效。
  • 心跳周期:指定心跳的发送周期,即两次心跳发送之间的时间间隔。
  • 失效重试次数:指定在一定时间内连续检测失效的次数,达到该次数则判定服务提供者为失效。
  • 失效恢复时间:指定在服务提供者失效后,尝试恢复其可用状态的时间范围。

如何解决服务调用链过长的问题?

  • 负载均衡:Dubbo 支持多种负载均衡策略,如随机、轮询、一致性哈希等。选择合适的负载均衡策略可以有效地分散请求,减轻单个服务提供者的负担,从而减少调用链长度。根据业务情况选择合适的负载均衡策略。
  • 集群划分:如果服务规模较大,可以将服务提供者按照功能划分为不同的集群,每个集群负责不同的业务功能。这样可以减少单个集群内的调用链长度,提高系统的可维护性和扩展性。
  • 异步调用:对于一些不需要立即返回结果的调用,可以使用异步调用来减少调用链长度。Dubbo提供了异步调用的支持,允许消费者发起调用后立即返回,而服务提供者在后台处理请求并将结果返回给消费者。
  • 服务拆分:如果一个服务的功能过于复杂,可以考虑将其拆分为多个微服务,每个微服务只负责特定的功能。这样可以有效地缩短调用链,降低耦合性,并提高系统的灵活性和可维护性。
  • 缓存:对于一些频繁被调用的服务,可以在消费者端使用缓存来存储结果。这样可以避免频繁的远程调用,减少调用链长度,提高性能。
  • 网络优化:调用链过长可能会增加网络延迟。可以通过优化网络拓扑、使用更高速的网络设备以及采用合适的通信协议来减少网络延迟,从而降低调用链长度对性能的影响。
  • 降级与容错:在调用链中的某个环节出现问题时,可以通过实现降级和容错机制来保证系统的稳定性。Dubbo 提供了丰富的容错和降级策略,如熔断、超时设置、失败重试等。
  • 性能监控与优化:定期监控和分析系统的性能指标,及时发现调用链过长的问题。可以使用监控工具来追踪服务调用链,发现瓶颈,然后针对性地进行优化。

Dubbo 服务暴露的原理?

Dubbo 会在 Spring 实例化完 bean之后,在刷新容器最后一步发布 ContextRefreshEvent 事件的时候,通知实现了 ApplicationListener 的 ServiceBean 类进行回调 onApplicationEvent 事件方法,Dubbo 会在这个方法中调用 ServiceBean 父类 ServiceConfig 的 export 方法,而该方法真正实现了服务的(异步或者非异步)发布。

Dubbo 和 Dubbox 的区别?

Dubbox 是继 Dubbo 停止维护后,当当网基于 Dubbo 做的一个扩展项目、如加了服务可 Restful 调用、更新了开源组件等。

Hessian 的数据结构?

Hessian 的对象序列化机制有 8 种原始类型:

  • 原始⼆进制数据
  • boolean
  • 64-bit date(64 位毫秒值的⽇期)
  • 64-bit double
  • 32-bit int
  • 64-bit long
  • null
  • UTF-8 编码的 string

另外还包括 3 种递归类型:

  • list for lists and arrays
  • map for maps and dictionaries
  • object for objects

还有⼀种特殊的类型:

  • ref:用来表示对共享对象的引用。

Dubbo 注册中心

Dubbo 支持哪些注册中心?

注册中心是 Dubbo 服务治理的核心组件,Dubbo 目前支持的主流注册中心包括:

  • Zookeeper(默认)
  • Nacos
  • Redis
  • Multicast
  • Simple(在 Dubbo 2.7 中已移除)

同时也支持 Kubernetes、Mesh 体系的服务发现,另外,Dubbo 扩展生态还提供了 Consul、Eureka、Etcd 等注册中心扩展实现。

Dubbo 支持在一个应用中指定多个注册中心,并将服务根据注册中心分组,这样做使得服务分组管理或服务迁移变得更容易。

Dubbo 中 ZK 做注册中心,如果 ZK 集群都挂掉,发布者和订阅者之间还能通信么?

可以通信。
启动 Dubbo 时,消费者会从 ZK 拉取注册的生产者的地址接口等数据并缓存在本地,每次调用时,按照本地存储的地址进行调用。

  • 注册中心对等集群,任意一台宕机后,将会切换到另一台。
  • 注册中心全部宕机后,服务的提供者和消费者仍能通过本地缓存通讯。
  • 服务提供者无状态,任一台宕机后,不影响使用。
  • 服务提供者全部宕机,服务消费者会无法使用并无限次重连等待服务者恢复。

挂掉是不要紧的,但前提是你没有增加新的服务,如果你要调用新的服务,则是不能办到的。

另外:

  • 监控中心宕机不会影响使用,只是会丢失部分采样数据。
  • 数据库宕机后,注册中心仍能通过缓存提供的服务列表查询,但是不能注册新服务。

Dubbo 连接注册中心和直连的区别?

在 Dubbo 框架中,连接注册中心和直连是实现服务消费者与服务提供者之间通信的两种方式。

(1)Dubbo 连接注册中心

Dubbo 连接注册中心是指通过注册中心来管理和维护服务提供者与服务消费者之间的关系。当服务提供者上线或下线时,注册中心能够感知到并及时更新服务的状态信息。

Dubbo 连接注册中心的工作流程如下:

  1. 服务提供者将自己的地址、端口等信息注册到注册中心。
  2. 服务消费者从注册中心获取服务提供者的地址列表。
  3. 服务消费者根据负载均衡算法选择一个地址进行连接。
  4. 服务消费者与选定的服务提供者建立连接,进行通信。

Dubbo 连接注册中心的优点:

  • 服务自动发现和负载均衡:通过连接注册中心,服务消费者可以自动发现可用的服务提供者,并根据负载均衡算法选择合适的提供者进行访问。这样可以有效地分摊服务请求,提高系统的整体性能和可扩展性。
  • 服务状态管理和监控:注册中心可以实时监控服务提供者的状态,包括上线、下线等。当服务提供者异常退出或服务不可用时,注册中心可以及时更新服务的状态信息,以保证服务调用的稳定性和可用性。
  • 服务治理能力强:Dubbo 连接注册中心提供了丰富的服务治理能力,如路由、限流、容错等机制。通过配置注册中心,可以灵活地控制服务的访问策略,提高系统对于故障和性能问题的容错和处理能力。

连接注册中心适用于大规模的分布式系统,需要自动发现、负载均衡和动态路由等功能,并对服务状态进行监控和管理的情况。

(2)Dubbo直连

Dubbo 直连是指服务消费者直接与指定的服务提供者建立连接,跳过注册中心的过程。

Dubbo 直连的工作流程如下:

  1. 服务提供者将自己的地址、端口等信息提供给服务消费者。
  2. 服务消费者使用提供的地址和端口直接与服务提供者建立连接。
  3. 服务消费者与服务提供者建立连接,进行通信。

Dubbo 直连的优点:

  • 简化部署和配置:直连模式下,服务消费者无需依赖注册中心,简化了部署和配置的过程。尤其在开发和测试环境中,可以直接与指定的服务提供者进行通信,提高了开发效率和测试灵活性。
  • 较低的网络开销:由于直连模式跳过了注册中心的过程,服务消费者直接与服务提供者建立连接,减少了网络开销。对于一些对实时性要求较高的场景,直连模式能够提供更好的性能表现。

Dubbo 直连的缺点:

  • 缺乏自动发现和负载均衡:直连模式下,服务消费者需要手动指定服务提供者的地址和端口,无法享受到注册中心自动发现和负载均衡的功能。这在大规模的分布式系统中可能会引起服务调用的不均衡和单点故障风险。
  • 服务状态管理和监控的局限性:直连模式下,无法像连接注册中心一样对服务的状态进行实时监控和管理。当服务提供者发生故障或下线时,服务消费者无法及时感知,可能导致请求失败或延迟增加。

直连适用于开发和测试环境中,或者对网络实时性要求较高、规模较小、服务提供者固定的场景。

Dubbo 协议

Dubbo 支持哪些协议?

Dubbo 支持 Dubbo、Triple 、REST、gRPC、HTTP、Thrift、Rmi、Redis、Hessian、Hessian2、Webservice、Memcached、JsonRPC 等多种协议。

Dubbo2 默认使用 Dubbo 协议,Triple 协议是 Dubbo3 推出的主力协议。Triple 意为第三代,通过 Dubbo1.0/ Dubbo2.0 两代协议的演进,以及云原生带来的技术标准化浪潮,Dubbo3 新协议 Triple 应运而生。

(1)Dubbo 协议

Dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。Dubbo 协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。

(2)Triple 协议

Triple 是 Dubbo3 提出的基于 HTTP 的开放协议,旨在解决 Dubbo2 私有协议带来的互通性问题,Tripe 基于 gRPC 和 gRPC-Web 设计而来,保留了两者的优秀设计,Triple 做到了完全兼容 gRPC 协议,并可同时运行在 HTTP/1 和 HTTP/2 之上。

相比于原有 Dubbo2 协议,Triple 有以下优势:

  • 原生和 gRPC 协议互通。打通 gRPC 生态,降低从 gRPC 至 Dubbo 的迁移成本。
  • 增强多语言生态。避免因 CPP/C#/RUST 等语言的 Dubbo SDK 能力不足导致业务难以选型适配的问题。
  • 网关友好。网关无需参与序列化,方便用户从传统的 HTTP 转泛化 Dubbo 调用网关升级至开源或云厂商的 Ingress 方案。
  • 完善的异步和流式支持。带来从底层协议到上层业务的性能提升,易于构建全链路异步以及严格保证消息顺序的流式服务。

相比于 gRPC 协议,Triple 有以下优势:

  • 协议内置支持 HTTP/1,可以用 curl、浏览器直接访问你的 gRPC 服务
  • 保持性能与 grpc-java 在同一水平的同时,实现上更轻量、简单,协议部分只有几千行代码
  • 不绑定 IDL,支持 Java Interface 定义服务
  • 保持与官方 gRPC 库的 100% 兼容性的同时,与 Dubbo 的微服务治理体系无缝融合

(3)Rest 协议

基于标准的 Java REST API——JAX-RS 2.0(Java API for RESTful Web Services 的简写)实现的 REST 调用支持

此协议提供通过 web 访问服务的简单方式,将服务与其他基于 web 的应用程序集成。 支持 JSON、XML 和 Text 格式的请求和响应,发布和使用服务的便捷方式,也提供了服务版本控制、服务过滤、服务元数据和服务参数, 实现 Dubbo 框架的灵活性和可伸缩性。

(4)gRPC 协议

Dubbo 自 2.7.5 版本开始支持 gRPC 协议,对于计划使用 HTTP/2 通信,或者想利用 gRPC 带来的 Stream、反压、Reactive 编程等能力的开发者来说, 都可以考虑启用 gRPC 协议。

支持 gRPC 的好处:

  • 为期望使用 gRPC 协议的用户带来服务治理能力,方便接入 Dubbo 体系
  • 用户可以使用 Dubbo 风格的,基于接口的编程风格来定义和使用远程服务

gRPC 使用场景:

  • 需要立即响应才能继续处理的同步后端微服务到微服务通信。
  • 需要支持混合编程平台的 Polyglot 环境。
  • 性能至关重要的低延迟和高吞吐量通信。
  • 点到点实时通信 - gRPC 无需轮询即可实时推送消息,并且能对双向流式处理提供出色的支持。
  • 网络受约束环境 - 二进制 gRPC 消息始终小于等效的基于文本的 JSON 消息。

(5)HTTP 协议

基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现,2.3.0 以上版本支持。

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:表单序列化
  • 适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或 URL 传入参数,暂不支持传文件。
  • 适用场景:需同时给应用程序和浏览器 JS 使用的服务。

(6)Thrift 协议

当前 Dubbo 支持的 thrift 协议是对 thrift 原生协议的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。2.3.0 以上版本支持。

Thrift 是 Facebook 捐给 Apache 的一个 RPC 框架。

使用 Dubbo Thrift 协议同样需要使用 Thrift 的 idl compiler 编译生成相应的 Java 代码,后续版本中会在这方面做一些增强。

(7)Rmi 协议

RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:TCP
  • 传输方式:同步传输
  • 序列化:Java 标准二进制序列化
  • 适用范围:传入传出参数数据包大小混合,消费者与提供者个
  • 数差不多,可传文件。
  • 适用场景:常规远程服务方法调用,与原生RMI服务互操作

使用 RMI 协议时,参数及返回值需实现 Serializable 接口,并且 Dubbo 配置中的超时时间对 RMI 无效,需使用 Java 启动参数设置:-Dsun.rmi.transport.tcp.responseTimeout=3000。

(8)Redis 协议

基于 Redis 实现的 RPC 协议,用于缓存,限流,分布式锁等场景。

(9)Hessian 协议

Hessian 协议用于集成 Hessian 的服务,底层采用 Http 通讯,通过 Servlet 暴露服务,Dubbo 默认内嵌 Jetty 作为服务器实现。

Hessian 是 Caucho 开源的一个轻量级的 RPC 框架,其通讯效率高于 WebService 和 Java 自带的序列化。

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:Hessian二进制序列化
  • 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。
  • 适用场景:页面传输,文件传输,或与原生hessian服务互操作。

Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:

  • 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用,
  • 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。

使用 Hessian 协议时,参数及返回值需实现 Serializable 接口。参数及返回值不能自定义实现 List、Map、Number、Date、Calendar 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。

(10)Webservice协议

基于 WebService 的远程调用协议,基于 Apache CXF 的 frontend-simple 和 transports-http 实现。2.3.0 以上版本支持。

CXF 是 Apache 开源的一个 RPC 框架,由 Xfire 和 Celtix 合并而来。

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:SOAP 文本序列化
  • 适用场景:系统集成,跨语言调用

可以和原生 WebService 服务互操作,即:

  • 提供者用 Dubbo 的 WebService 协议暴露服务,消费者直接用标准 WebService 接口调用,
  • 或者提供方用标准 WebService 暴露服务,消费方用 Dubbo 的 WebService 协议调用。

使用 webservice 协议时,参数及返回值需实现 Serializable 接口,参数尽量使用基本类型和 POJO

发布一个服务(对内/对外),不考虑客户端类型,不考虑性能,建议使用 webservice。服务端已经确定使用 webservice,客户端不能选择,必须使用 webservice。

(11)Memcached 协议

基于 memcached 实现的 RPC 协议。

Dubbo 协议使用方式?

配置协议:

<dubbo:protocol name="dubbo" port="20880" />

设置默认协议:

<dubbo:provider protocol="dubbo" />

设置某个服务的协议:

<dubbo:service interface="..." protocol="dubbo" />

多端口:

<dubbo:protocol id="dubbo1" name="dubbo" port="20880" />
<dubbo:protocol id="dubbo2" name="dubbo" port="20881" />

配置协议选项:

<dubbo:protocol name="dubbo" 
                port="9090" 
                server="netty" 
                client="netty" 
                codec="dubbo" 
                serialization="hessian2" 
                charset="UTF-8" 
                threadpool="fixed" 
                threads="100" 
                queues="0" 
                iothreads="9" 
                buffer="8192" 
                accepts="1000" 
                payload="8388608" />

多连接配置:Dubbo 协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。

<dubbo:service interface="..." connections="1"/>
<dubbo:reference interface="..." connections="1"/>
  • <dubbo:service connections="0"> 或 <dubbo:reference connections="0"> 表示该服务使用 JVM 共享长连接。缺省
  • <dubbo:service connections="1"> 或 <dubbo:reference connections="1"> 表示该服务使用独立长连接。
  • <dubbo:service connections="2"> 或<dubbo:reference connections="2"> 表示该服务使用独立两条长连接。

为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护。

<dubbo:protocol name="dubbo" accepts="1000" />

Dubbo 协议为什么不能传大包?

因 Dubbo 协议采用单一长连接,如果每次请求的数据包大小为 500KByte,假设网络为千兆网卡 1024Mbit=128MByte,每条连接最大 7MByte (不同的环境可能不一样),单个服务提供者的 TPS(每秒处理事务数)最大为:128MByte / 500KByte = 262。单个消费者调用单个服务提供者的 TPS (每秒处理事务数)最大为:7MByte / 500KByte = 14。如果能接受,可以考虑使用,否则网络将成为瓶颈。

多以 Dubbo 协议传入传出数据包大小一般建议小于100K。

Dubbo 协议为什么采用异步单一长连接?

因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务。

比如 Morgan 的提供者只有 6 台提供者,却有上百台消费者,每天有 1.5 亿次调用,如果采用常规的 hessian 服务,服务提供者很容易就被压跨。

通过单一连接,保证单一消费者不会压死提供者,长连接可以减少连接握手验证等,并使用异步 IO,复用线程池,防止 C10K 问题。

Dubbo 支持哪些序列化协议?

说⼀下 Hessian 的数据结构?

为什么 PB 的效率是最高的?

Dubbo 实现机制

Dubbo 的重试机制?

Dubbo 允许在远程调用失败时进行重试,以增加调用的成功率。以下是一些重试相关的配置选项:

  • retries:配置重试次数,默认为2次。如果设置为0,则表示不进行重试。
  • timeout:每次重试的超时时间,可以单独设置,以覆盖全局超时时间。
  • retries 和 timeout 的组合可以用来调整重试的行为,以应对不同的失败场景。

xml 文件方式配置:

## 提供方
# 全局配置
<dubbo:provider timeout="超时时间" retries="2"></dubbo:provider>

# 接口级配置
<dubbo:service interface="XXXXX" ref="XXXXX" timeout="超时时间" retries="重试次数">
    <dubbo:method name="XXXXX"></dubbo:method>
</dubbo:service>

# 方法级配置
<dubbo:service interface="XXXXXX" ref="XXXXX" >
    <dubbo:method name="XXXXX" timeout="超时时间" retries="重试次数"></dubbo:method>
</dubbo:service>

## 消费方
# 全局配置
<dubbo:consumer timeout="超时时间" retries="重试次数"></dubbo:consumer>

# 接口级配置
<dubbo:reference interface="XXXXXXX" id="XXXXXX" timeout="超时时间" retries="重试次数">
    <dubbo:method name="XXXXXX" timeout="3000" retries="2"></dubbo:method>
</dubbo:reference>

# 方法级配置
<dubbo:reference interface="XXXXX" id="XXXXX">
    <dubbo:method name="XXXXX" timeout="超时时间" retries="重试次数"></dubbo:method>
</dubbo:reference>

配置文件方式配置:

# 提供方
dubbo.consumer.retries=2
dubbo.consumer.timeout=5000

# 消费方
dubbo.consumer.retries=2
dubbo.consumer.timeout=5000

注解方式配置:

# 提供者方
@Service(retries = 3, timeout = 3000)

# 消费者方
# check 默认为true,启动时检查服务是否可用
@Reference(retries = 3, timeout = 3000, check = false)

Dubbo 的容错机制?

Dubbo 提供了多种容错处理策略,以应对不同类型的服务调用失败。以下是一些常见的容错处理策略:

Failover:失败自动切换

  • 默认的容错策略。当调用失败时,Dubbo 会自动切换到其他可用的服务提供者,以尝试调用成功。可通过 retries="2"来设置重试次数
  • 适用于高可用性要求较高的场景。

Failfast:快速失败

  • 一旦发生调用失败,Dubbo 会立即抛出异常,不进行重试。
  • 适用于对性能要求较高和非幂等性的写操作场景。

Failback:失败自动恢复

  • 当调用失败时,Dubbo 会将失败的请求记录下来,在后续请求中再次尝试调用。
  • 适用于不要求实时性的场景,如消息通知。

Failsafe:失败安全

  • 当调用失败时,Dubbo 会记录失败的请求,但不会抛出异常,而是返回一个默认值。
  • 适用于某些失败不重要的场景,如写入日志。

Failover 与 Failback 组合:

  • 可以将 Failover 和 Failback 结合使用,以兼顾高可用性和容错恢复的需求。

Forking:并行调用多个提供者

  • Dubbo 会同时调用多个服务提供者,只要有一个成功即返回结果。可通过 forks="2" 来设置最大并行数。
  • 适用于提高调用成功率的场景,但会增加资源开销。

Broadcast:广播调用所有提供者

  • Dubbo 会调用所有服务提供者,然后将所有结果聚合起来。
  • 适用于广播通知等场景。

自定义容错策略:

  • Dubbo 还允许开发人员实现自定义的容错策略,以满足特定的业务需求。

Dubbo 负载均衡机制?

Dubbo提供了多种负载均衡机制,用于在多个服务提供者之间均衡分发调用请求,以提高系统的性能和可用性。以下是 Dubbo 中常见的负载均衡机制:

Random LoadBalance:随机负载均衡

  • 按权重设置随机概率(权重可以在 Dubbo 管控台配置),随机选择一个可用的服务提供者来处理请求。
  • 适用于轻负载的场景,每个提供者有相同的机会被选中,但不保证均衡负载。
  • 调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

Round Robin LoadBalance:轮询负载均衡

  • 按公约后的权重设置轮循比率,每次选择下一个可用的服务提供者来处理请求,按照顺序循环。
  • 适用于各个提供者的性能相近的场景,可以均衡负载。
  • 存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

Least Active LoadBalance:最小活跃数负载均衡

  • 选择当前活跃数(正在处理请求的数量)最小的服务提供者来处理请求,相同活跃数的随机,活跃数指调用前后计数差。
  • 适用于提供者性能有差异的场景,可以降低高负载提供者的负载。

Consistent Hash LoadBalance:一致性哈希负载均衡

  • 使用一致性哈希算法将请求映射到特定的提供者节点,以确保相同参数的请求始终路由到同一个提供者。
  • 适用于需要保持某些请求的一致性,如缓存服务等。
  • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
  • 一致性哈希默认只对第一个参数 Hash,可以通过配置 <dubbo:parameter key="hash.arguments" value="0,1" /> 来改变。
  • 一致性哈希默认用 160 份虚拟节点,可以通过配置 <dubbo:parameter key="hash.nodes" value="320" /> 来改变。

Weighted LoadBalance:加权负载均衡

  • 根据提供者的权重配置来选择提供者,权重越高的提供者被选中的概率越高。
  • 适用于提供者性能不均衡的场景,可以根据性能分配不同的权重。

Local First LoadBalance:本地优先负载均衡

  • 如果有本地提供者(同一进程内),首先选择本地提供者来处理请求,如果没有再选择远程提供者。
  • 适用于优化本地调用性能的场景。

Adaptive LoadBalance:自适应负载均衡

  • 根据提供者的调用响应时间和活跃数等信息,动态选择合适的负载均衡策略。
  • 适用于不同场景下的动态负载均衡调整。

Dubbo 在安全机制方面如何实现?

Dubbo 通过 Token 令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。

Dubbo 还提供服务黑白名单,来控制服务所允许的调用方。
 

更多推荐