目录
1、Dubbo概念与架构
1.1 Dubbo简介
1.2 注册与发现流程图
1.3 Dubbo架构图
2、Dubbo调用流程
3、关于Dubbo 服务治理
3.1 Dubbo通信协议
3.2 序列化方式
3.3 负载均衡算法
4、Dubbo与Spring Cloud关系
4.1 相似之处
4.2 差异之处
5、Dubbo 与 gRPC 关系
6、其他常见零散面试问题
6.1 Dubbo 的容错策略有哪些?如何选择?
6.2 注册中心宕机,服务间是否可以继续通信?
6.3 Dubbo启动时如果依赖的服务不可用会怎样?
6.4 Dubbo 如何优雅停机?
6.4 Dubbo 用到哪些设计模式
1、Dubbo概念与架构
1.1 Dubbo简介
微服务的分布式特性使得应用间的依赖、网络交互、数据传输变得更频繁,因此不同的应用需要定义、暴露或调用 RPC 服务,那么这些 RPC 服务如何定义、如何与应用开发框架结合、服务调用行为如何控制?这就是 Dubbo 服务开发框架的含义,Dubbo 在微服务应用开发框架之上抽象了一套 RPC 服务定义、暴露、调用与治理的编程范式,比如 Dubbo Java 作为服务开发框架,当运行在 Spring 体系时就是构建在 Spring Boot 应用开发框架之上的微服务开发框架,并在此之上抽象了一套 RPC 服务定义、暴露、调用与治理的编程范式。
Dubbo 作为服务开发框架包含的具体内容如下:
- RPC 服务定义、开发范式:比如 Dubbo 支持通过 IDL 定义服务,也支持编程语言特有的服务开发定义方式,如通过 Java Interface 定义服务。
- RPC 服务发布与调用 API:Dubbo 支持同步、异步、Reactive Streaming 等服务调用编程模式,还支持请求上下文 API、设置超时时间等。
- 服务治理策略、流程与适配方式等:作为服务框架数据面,Dubbo 定义了服务地址发现、负载均衡策略、基于规则的流量路由、Metrics 指标采集等服务治理抽象,并适配到特定的产品实现。
1.2 注册与发现流程图
Dubbo主要提供了3大核心功能:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
1.3 Dubbo架构图
Dubbo三大组件:
- 注册中心:协调 Consumer 与 Provider 之间的地址注册与发现
- 配置中心:
- 存储 Dubbo 启动阶段的全局配置,保证配置的跨环境共享与全局一致性 。
- 负责服务治理规则(路由规则、动态配置等)的存储与推送。
- 元数据中心:
- 接收 Provider 上报的服务接口元数据,为 Admin 等控制台提供运维能力(如服务测试、接口文档等) 。
- 作为服务发现机制的补充,提供额外的接口/方法级别配置信息的同步能力,相当于注册中心的额外扩展
以下是 Dubbo 部署的经典架构图,由注册中心 (服务发现)、配置中心和元数据中心构成了整个服务治理的核心。
2、Dubbo调用流程
对照上面的整体架构图,大致分为以下8大步骤:
1、服务提供者启动,开启Netty服务,创建Zookeeper客户端,向注册中心注册服务;
2、服务消费者启动,通过Zookeeper向注册中心获取服务提供者列表,与服务提供者通过Netty建立长连接;
3、服务消费者通过接口开始远程调用服务,ProxyFactory通过初始化Proxy对象,Proxy通过创建动态代理对象;
4、动态代理对象通过invoke方法,层层包装生成一个Invoker对象,该对象包含了代理对象;
5、Invoker通过路由,负载均衡选择了一个最合适的服务提供者,在通过加入各种过滤器,协议层包装生成一个新的DubboInvoker对象;
6、再通过交换成将DubboInvoker对象包装成一个Reuqest对象,该对象通过序列化通过NettyClient传输到服务提供者的NettyServer端;
7、到了服务提供者这边,再通过反序列化、协议解密等操作生成一个DubboExporter对象,再层层传递处理,会生成一个服务提供端的Invoker对象;
8、这个Invoker对象会调用本地服务,获得结果再通过层层回调返回到服务消费者,服务消费者拿到结果后,再解析获得最终结果。
3、关于Dubbo 服务治理
服务开发框架解决了开发与通信的问题,但在微服务集群环境下,我们仍需要解决无状态服务节点动态变化、外部化配置、日志跟踪、可观测性、流量管理、高可用性、数据一致性等一系列问题,我们将这些问题统称为服务治理。
- 地址发现 :Dubbo 服务发现具备高性能、支持大规模集群、服务级元数据配置等优势,默认提供 Nacos、Zookeeper、Consul 等多种注册中心适配,与 Spring Cloud、Kubernetes Service 模型打通,支持自定义扩展。
- 负载均衡 :Dubbo 默认提供加权随机、加权轮询、最少活跃请求数优先、最短响应时间优先、一致性哈希和自适应负载等策略
- 流量路由 :Dubbo 支持通过一系列流量规则控制服务调用的流量分布与行为,基于这些规则可以实现基于权重的比例流量分发、灰度验证、金丝雀发布、按请求参数的路由、同区域优先、超时配置、重试、限流降级等能力。
- 链路追踪 :Dubbo 官方通过适配 OpenTelemetry 提供了对 Tracing 全链路追踪支持,用户可以接入支持 OpenTelemetry 标准的产品如 Skywalking、Zipkin 等。另外,很多社区如 Skywalking、Zipkin 等在官方也提供了对 Dubbo 的适配。
- 可观测性 :Dubbo 实例通过 Prometheus 等上报 QPS、RT、请求次数、成功率、异常次数等多维度的可观测指标帮助了解服务运行状态,通过接入 Grafana、Admin 控制台帮助实现数据指标可视化展示。
Dubbo 服务治理生态还提供了对 API 网关、限流降级、数据一致性、认证鉴权等场景的适配支持。
3.1 Dubbo通信协议
Dubbo 从设计上不绑定任何一款特定通信协议,HTTP/2、REST、gRPC、JsonRPC、Thrift、Hessian2 等几乎所有主流的通信协议,Dubbo 框架都可以提供支持。 这样的 Protocol 设计模式给构建微服务带来了最大的灵活性,开发者可以根据需要如性能、通用型等选择不同的通信协议,不再需要任何的代理来实现协议转换,甚至你还可以通过 Dubbo 实现不同协议间的迁移。
Dubbo Protocol 被设计支持扩展,您可以将内部私有协议适配到 Dubbo 框架上,进而将私有协议接入 Dubbo 体系,以享用 Dubbo 的开发体验与服务治理能力。
3.2 序列化方式
序列化方式与协议是息息相关的。常用的 Dubbo 序列化方式包括:
- Hessian2 序列化:Dubbo 默认的序列化方式,性能较差,但兼容性最好。
- Dubbo 序列化:阿里修改过的 Hessian2,优化了性能。
- FastJson 序列化:阿里巴巴开源的 JSON 序列化库,性能较好,但是可能会存在兼容问题。
- Java 原生序列化:性能较差,但是兼容性最好。
3.3 负载均衡算法
当多个provider注册到zk,consumer会选择指定的负载均衡算法自动请求,遇到上线或者下线会重新计算。 常见有四种负载均衡:
- random :随机,按权重设置随机概率。 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
- roundRobin (默认) :轮询,按公约后的权重设置轮询比率。 存在慢的提供者类即请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
- leastActive :最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
- consistentHash :一致性Hash,相同参数的请求总是发到同一提供者。 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其他提供者,不会引起剧烈变动。
4、Dubbo与Spring Cloud关系
4.1 相似之处
Dubbo 和 Spring Cloud 有很多相似之处,它们都在整个架构图的相同位置并提供一些相似的功能:
- Dubbo 和 Spring Cloud 都侧重在对分布式系统中常见问题模式的抽象(如服务发现、负载均衡、动态配置等),同时对每一个问题都提供了配套组件实现,形成了一套微服务整体解决方案,让使用 Dubbo 及 Spring Cloud 的用户在开发微服务应用时可以专注在业务逻辑开发上。
- Dubbo 和 Spring Cloud 都完全兼容 Spring 体系的应用开发模式,Dubbo 对 Spring 应用开发框架、Spring Boot 微服务框架都做了很好的适配,由于 Spring Cloud 出自 Spring 体系,在这一点上自然更不必多说。
虽然两者有很多相似之处,但由于它们在诞生背景与架构设计上的巨大差异,两者在性能、适用的微服务集群规模、生产稳定性保障、服务治理等方面都有很大差异。
4.2 差异之处
Spring Cloud 的优势在于:
- 同样都支持 Spring 开发体系的情况下,Spring Cloud 得到更多的原生支持
- 对一些常用的微服务模式做了抽象如服务发现、动态配置、异步消息等,同时包括一些批处理任务、定时任务、持久化数据访问等领域也有涉猎。
- 基于 HTTP 的通信模式,加上相对比较完善的入门文档和演示 demo 和 starters,让开发者在第一感觉上更易于上手
Spring Cloud 的问题有:
- 只提供抽象模式的定义不提供官方稳定实现,开发者只能寻求类似 Netflix、Alibaba、Azure 等不同厂商的实现套件,而每个厂商支持的完善度、稳定性、活跃度各异
- 有微服务全家桶却不是能拿来就用的全家桶,demo 上手容易,但落地推广与长期使用的成本非常高
- 欠缺服务治理能力,尤其是流量管控方面如负载均衡、流量路由方面能力都比较弱
- 编程模型与通信协议绑定 HTTP,在性能、与其他 RPC 体系互通上存在障碍
- 总体架构与实现只适用于小规模微服务集群实践,当集群规模增长后就会遇到地址推送效率、内存占用等各种瓶颈的问题,但此时迁移到其他体系却很难实现
- 很多微服务实践场景的问题需要用户独自解决,比如优雅停机、启动预热、服务测试,再比如双注册、双订阅、延迟注册、服务按分组隔离、集群容错等
而以上这些点,都是 Dubbo 的优势所在:
- 完全支持 Spring & Spring Boot 开发模式,同时在服务发现、动态配置等基础模式上提供与 Spring Cloud 对等的能力。
- 是企业级微服务实践方案的整体输出,Dubbo 考虑到了企业微服务实践中会遇到的各种问题如优雅上下线、多注册中心、流量管理等,因此其在生产环境的长期维护成本更低
- 在通信协议和编码上选择更灵活,包括 rpc 通信层协议如 HTTP、HTTP/2(Triple、gRPC)、TCP 二进制协议、rest等,序列化编码协议Protobuf、JSON、Hessian2 等,支持单端口多协议。
- Dubbo 从设计上突出服务服务治理能力,如权重动态调整、标签路由、条件路由等,支持 Proxyless 等多种模式接入 Service Mesh 体系
- 高性能的 RPC 协议编码与实现
- Dubbo 是在超大规模微服务集群实践场景下开发的框架,可以做到百万实例规模的集群水平扩容,应对集群增长带来的各种问题
- Dubbo 提供 Java 外的多语言实现,使得构建多语言异构的微服务体系成为可能
5、Dubbo 与 gRPC 关系
Dubbo 与 gRPC 最大的差异在于两者的定位上:
- gRPC 定位为一款 RPC 框架,Google 推出它的核心目标是定义云原生时代的 rpc 通信规范与标准实现;
- Dubbo 定位是一款微服务开发框架,它侧重解决微服务实践从服务定义、开发、通信到治理的问题,因此 Dubbo 同时提供了 RPC 通信、与应用开发框架的适配、服务治理等能力。
Dubbo 不绑定特定的通信协议,即 Dubbo 服务间可通过多种 RPC 协议通信并支持灵活切换。因此,你可以在 Dubbo 开发的微服务中选用 gRPC 通信,Dubbo 完全兼容 gRPC,并将 gRPC 设计为内置原生支持的协议之一(推荐的使用模式 Dubbo + gRPC 的组合)。
6、其他常见零散面试问题
6.1 Dubbo 的容错策略有哪些?如何选择?
Dubbo 的容错策略有多种,包括 Failover 容错策略、Failfast 容错策略、Failback 容错策略、Failsafe 容错策略和Forking 容错策略等。在选择容错策略时,需要根据具体的业务场景和需求来选择。
6.2 注册中心宕机,服务间是否可以继续通信?
可以通信的,启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用。但前提是你没有增加新的服务,如果你要调用新的服务,则是不能办到的。另外如果服务的提供者全部宕机,服务消费者会无法使用,并无限次重连等待服务者恢复。
6.3 Dubbo启动时如果依赖的服务不可用会怎样?
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,默认 check=“true”,可以通过 check=“false” 关闭检查。
6.4 Dubbo 如何优雅停机?
Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果使用kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。
6.4 Dubbo 用到哪些设计模式
Dubbo 框架在初始化和通信过程中使用了多种设计模式,可灵活控制类加载、权限控制等功能。
- 装饰器模式:Dubbo 在启动和调用阶段都大量使用了装饰器模式。以 Provider 提供的调用链为例,具体的调用链代码是在 ProtocolFilterWrapper 的buildInvokerChain 完成的,具体是将注解中含有 group=provider 的 Filter 实现 ,按照 order 排序 ,最后的调用顺序是:EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter ->ExecuteLimitFilter -> TraceFilter -> TimeoutFilter -> MonitorFilter ->ExceptionFilter
- 观察者模式:Dubbo 的 Provider 启动时,需要与注册中心交互,先注册自己的服务,再 订阅自己的服务,订阅时,采用了观察者模式,开启一个 listener。注册中心会每 5 秒定时检查是否有服务更新,如果有更新,向该服务的提供者发送一个 notify 消息,provider 接受到 notify 消息后,即运行 NotifyListener 的 notify 方法,执行监听器方法。
- 动态代理模式:Dubbo 扩展 JDK SPI 的类 ExtensionLoader 的 Adaptive 实现是典型的动态代理实现。Dubbo 需要灵活地控制实现类,即在调用阶段动态地根据参数决定调用哪个实现类,所以采用先生成代理类的方法,能够做到灵活的调用。生成代理类的代码是 ExtensionLoader 的 createAdaptiveExtensionClassCode 方法 。代理类的主要逻辑是,获取 URL 参数中指定参数的值作为获取实现类的 key。