微服务面试必问的Dubbo,这么详细还怕自己找不到工作(中)

1,302 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

大家好,我是小羽。

今天继续跟着小羽一起看看这个微服务框架之一的 Dubbo 的详细解读吧,没看第一章节的可以点击这里,鉴于文章篇幅过长,我会在这里分为三个章节,以下是关于第二章节的图文详解内容。

服务治理

治理原因

Dubbo的服务治理主要原因:

1、 过多的服务 URL 配置困难

2、 负载均衡分配节点压力过大的情况下也需要部署集群。

3、 服务依赖混乱,启动顺序不清晰。

4、 过多服务导致性能指标分析难度较大,需要监控。

主要特性

透明远程调用: 就像调用本地方法一样调用远程方法;只需简单配置,没有任何 API 侵入

负载均衡机制: Client 端 LB,可在内网替代 F5 等硬件负载均衡器

容错重试机制: 服务 Mock 数据,重试次数、超时机制等

自动注册发现: 注册中心基于接口名查询服务提 供者的 IP 地址,并且能够平滑添加或删除服务提供者

性能日志监控: Monitor 统计服务的调用次调和调用时间的监控中心

服务治理中心: 路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等手动配置

自动治理中心: 无,比如:熔断限流机制、自动权重调整等(因此可以搭配SpringCloud的熔断机制等进行开发)

​编辑

服务治理

架构设计

整体架构

先看下 Dubbo 的整体架构图:

图例说明:

​编辑

整体架构

图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。

图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,ServiceConfig 层为 API,其它各层均为 SPI。

图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。

图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。

各层说明

config 配置层: 对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 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

exchange 信息交换层: 封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer

transport 网络传输层: 抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec

serialize 数据序列化层: 可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

主要模块

dubbo-common 公共逻辑模块,包括 Util 类和通用模型

dubbo-remoting 远程通讯模块,相当于 Dubbo 协议的实现,如果 RPC 用 RMI 协议则不需要使用此包。

dubbo-rpc 远程调用模块,抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。

dubbo-cluster 集群模块,将多个服务提供方伪装为一个提供方,包括:负载均衡、容错、路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。

dubbo-registry 注册中心模块,基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。

dubbo-monitor 监控模块,统计服务调用次数,调用时间的,调用链跟踪的服务。

dubbo-config 配置模块,是 Dubbo 对外的 API ,用户通过 Config 使用 Dubbo ,隐藏 Dubbo 所有细节。

dubbo-container 容器模块,是一个 Standalone 的容器,以简单的 Main 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。

​编辑

主要模块

调用方式

异步调用

基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小

​编辑

异步调用

本地调用

使用了Injvm协议,是一个伪协议,它不开启端口,不发起远程调用,只在JVM内直接关联,但执行Dubbo的Filter链。

Define injvm protocol:

<dubbo:protocol name="injvm" /> 

Set default protocol:

<dubbo:provider protocol="injvm" />

Set service protocol:

<dubbo:service protocol="injvm" />

Use injvm first:(服务暴露与服务引用都需要声明injvm=“true”)

<dubbo:consumer injvm="true" .../>
<dubbo:provider injvm="true" .../>
或
<dubbo:reference injvm="true" .../>   <dubbo:service injvm="true" .../>

容错机制

调用流程

1、 Cluster 将 Directory 中的多个 Invoker 伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑

2、 Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等

3、 LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法

​编辑

调用流程

容错策略

Dubbo 官网提出总共有六种容错策略

1、 Failover Cluster

失败自动切换,当出现失败,重试其它服务器。(默认)

2、 Failfast Cluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

3、 Failsafe Cluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

4、 Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

5、 Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
可通过 forks=”2”来设置最大并行数。

6、 Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0 开始支持) 通常用于通知所有提供者更新缓存或日志等本地资源信息。

总结:在实际应用中查询语句容错策略建议使用默认 Failover Cluster,而增删改建议使用 Failfast Cluster 或者使用 Failover Cluster(retries=”0”)策略,防止出现数据重复添加等等其它问题!建议在设计接口时候把查询接口方法单独做一个接口提供查询。

连接方式

Dubbo 的客户端和服务端有三种连接方式,分别是:广播、直连和使用Zookeeper注册中心。

Dubbo 广播

这种方式是dubbo官方入门程序所使用的连接方式,但是这种方式有很多问题,在企业开发中不使用广播的方式。

服务端配置:

<!--配制dubbo-->
<!--提供应用信息,用于计算依赖关系-->
<dubbo:application name="demo-service"/>
<!--使用multicast广播注册暴露服务地址-->
<dubbo:registry address="multicast://192.168.9.4:88888" />

<!--使用dubbo协议在20880端口暴露服务-->
<dubbo:protocol name="dubbo" port="20880"/>

<!--声明暴露的服务接口-->
<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />

客户端配置:

<!--配合dubbo-->
<!--提供应用信息,用于计算依赖关系-->
<dubbo:application name="demo-web"/>

<!--使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="multicast://19.188.8.9:8888"/>

<!--声明需要暴露的接口-->
<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" />

Dubbo 直连

这种方式在企业中一般在开发中环境中使用,但是生产环境很少使用,因为服务是直接调用,没有使用注册中心,很难对服务进行管理。Dubbo 直连,首先要取消广播,然后客户端直接到指定需要的服务的 url 获取服务即可。

服务端配置:

<!--配制dubbo-->
<!--提供应用信息,用于计算依赖关系-->
<dubbo:application name="demo-service"/>
<!--使用multicast广播注册暴露服务地址-->
<-- <dubbo:registry address="multicast://192.168.9.4:88888" /> -->
<dubbo:registry adress="N/A">

<!--使用dubbo协议在20880端口暴露服务-->
<dubbo:protocol name="dubbo" port="20880"/>

<!--声明暴露的服务接口-->
<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />

客户端配置:

<!--配合dubbo-->
<!--提供应用信息,用于计算依赖关系-->
<dubbo:application name="demo-web"/>

<!--使用multicast广播注册中心暴露服务地址 -->
<-- <dubbo:registry address="multicast://19.188.8.9:8888"/> -->

<!--声明需要暴露的接口-->
<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" url="dubbo://127.0.0.1:20880" />

zookeeper 注册中心

Dubbo 注册中心和广播注册中心配置类似,不过需要指定注册中心类型和注册中心地址,这个时候就不是把服务信息进行广播了,而是告诉给注册中心进行管理,这个时候我们就需要有一个注册中心,官方推荐使用 zookeeper 作为注册中心。

​编辑

Zookeeper 注册中心

注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者在启动时与注册中心交互,消费者不断的发起请求获取服务信息,注册中心不转发请求,压力较小

服务端配置:

<!--配制dubbo-->
<!--提供应用信息,用于计算依赖关系-->
<dubbo:application name="demo-service"/>
<!--使用multicast广播注册暴露服务地址-->
<!-- <dubbo:registry address="multicast://192.168.9.4:88888" /> -->
<!--<dubbo:registry adress="N/A"> -->
<dubbo:registry protocol="zookeeper" address="192.168.37,136:2181">
<!--使用dubbo协议在20880端口暴露服务-->
<dubbo:protocol name="dubbo" port="20880"/>

<!--声明暴露的服务接口-->
<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />

客户端配置:

<!--配合dubbo-->
<!--提供应用信息,用于计算依赖关系-->
<dubbo:application name="demo-web"/>

<!--使用multicast广播注册中心暴露服务地址 -->
<-- <dubbo:registry address="multicast://19.188.8.9:8888"/> -->
<dubbo:registry protocol="zookeeper" address="192.168.37.1336:2181"/>    

<!--声明需要暴露的接口-->
<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" />

策略

负载均衡策略

1、 Random LoadBalance,随机(默认的负载均衡策略)

RandomLoadBalance 是加权随机算法的具体实现,可以完全随机,也可以按权重设置随机概率。

2、 RoundRobin LoadBalance,轮循

可以轮询和加权轮询。存在响应慢的提供者会累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

3、 LeastActive LoadBalance,最少活跃调用数

活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。

4、 ConsistentHash LoadBalance,一致性 Hash

一致性 Hash 算法,相同参数的请求一定分发到一个 provider 上去。provider 挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。

集群容错策略

1、 failover cluster(默认)

失败自动切换,调用失败时,自动重试其他机器。通常用于读操作,但重试会带来更长延迟。

2、 Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

3、 Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

4、 Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

5、 Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。

动态代理策略

默认使用 javassist 动态字节码生成,创建代理类。也可以通过 spi 扩展机制配置自己的动态代理策略。

最后

关于微服务框架中的Dubbo涉及到的内容还剩一部分,由于篇幅过长,会在最后一篇继续为大家带来图文方面的详细讲解,敬请期待~