Dubbo
Dubbo
1、解释一下RPC和HTTP的区别
RPC是Remote Procedure Call 远程过程调用的缩写,要想实现RPC通常需要包含传输协议和序列化协议。
而我们熟悉的HTTP,其实是一种超文本传输协议,RPC和HTTP不是同一个维度上的概念,只不过他们都是用来实现远程调用的,所以经常被拿来对比。
RPC适用于内部服务之间的相互调用,性能好,传输效率高,服务治理效果好。
而HTTP适合外部系统对接,比如浏览器访问、APP后端接口调用、三方接口对接等。
2、说说dubbo的优雅停机
Dubbo是通过jdk的shutdownhook来实现优雅停机的。
服务提供者,先将应用下线,标记为不接受请求,然后检查线程池中是否有正在运行的线程,若有,等待执行完成。
服务消费者,不再发起新的调用请求,若有新发起,直接在客户端侧报错,检查是否有正在远程调用的请求,若有已经发起且正在等待响应的,则等待响应完成。
3、dubbo支持哪些协议?
dubbo支持9种协议,包括dubbo(默认协议)、rmi、http、hessian、redis、memcached、thrift、webservice、restful这些协议。
dubbo协议的特点是:单一长连接,TCP协议传输,NIO异步通信,适合并发量高且数据传输量小的场景以及消费者远多于提供者的场景。
其它协议的特点不需要死记硬背。
4、dubbo的缓存机制了解吗?
dubbo支持客户端缓存和服务端缓存。
服务端缓存指的是将响应结果保存到服务端内存,下一次相同调用直接从内存中获得缓存并返回,支持多种缓存方式:LRU、ThreadLocal等等。
客户端缓存是指将远程调用服务方法的返回结果缓存到客户端内存,下次请求时直接从缓存中获取结果,而不必再调用远程服务方法。
缓存的目的都是为了减少重复调用和网络IO次数,提高系统响应性能。
5、dubbo如何实现像本地方法一样调用远程方法的?
Dubbo实现像本地方法一样调用远程方法的核心技术是动态代理。Dubbo使用JDK动态代理或者字节码增强技术,基于本地接口生成了一个代理类,这个代理类具有本地接口的所有方法。在调用本地接口方法时,就会通过代理类的invoke方法将请求转发到远程服务提供者上。这个过程包含了一系列环节:生成代理类、序列化和反序列化、网络通信、服务注册与发现、负载均衡、远程服务执行。
生成代理类:dubbo在启动时会扫描配置文件(注解)中指定的服务接口,并根据服务接口生成一个代理类。这个代理类实现了服务接口,并且在调用服务接口的方法时,会将参数封装为请求消息,然后通过网络传输给服务提供方。
序列化与反序列化:为了在网络上发送和接收数据,Dubbo会将方法调用的参数和返回值进行序列化(转换为字节序列)和反序列化(从字节序列还原为数据)。这里面可以使用多种序列化协议:如hessian、jdk自带的序列化、json等。
网络通信: dubbo支持多种通信协议,例如dubbo、http、rmi、redis、webservice、thrift、hessian等等协议,默认是dubbo协议。dubbo会根据协议的不同,选择不同的序列化方式,将请求消息序列化成二进制流并发送给服务提供方。
服务注册与发现:dubbo使用注册中心(zookeeper、nacos等)来管理服务的提供者和消费者的元数据信息。服务提供者在启动时将自己提供的服务地址信息注册到注册中心,服务消费者通过注册中心查找所需的服务并获取服务提供者的地址,然后通过一系列策略(轮询、一致性哈希、随机)将业务请求路由到目标服务提供者地址。
负载均衡: dubbo支持多种负载均衡算法,包括轮询、随机、加权随机、最小活跃数等,在客户端发起调用时,Dubbo会根据负载均衡算法选择一台服务提供者进行调用。
远程服务执行:当客户端发起远程调用时,服务提供方接收到请求后,会根据请求的服务接口名和方法,找到对应的实现类和方法,并将请求消息反序列化成参数列表,最终调用服务实现类的方法,并将执行结果序列化成响应消息返回给客户端。
6、说说dubbo的架构
从历史发展的角度来答:阿里巴巴开源的一个基于 Java 的 RPC 框架,在2017年重新开始维护,并且在 2018 年和当当的 Dubbox 进行了合并,进入 Apache 孵化器,在 2019 年毕业正式成为 Apache 顶级项目。 Dubbo 社区主力维护的是 2.6.x 和 2.7.x 两大版本,2.6.x 版本主要是 bug 修复和少量功能增强为准,是稳定版本。最新的 3.x 版本往云原生方向上探索着。
从总体架构的角度维护:分为如下角色节点
节点 | 角色说明 |
---|---|
Consumer | 需要调用远程服务的服务消费方 |
Registry | 注册中心 |
Provider | 服务提供方 |
Container | 服务运行的容器 |
Monitor | 监控中心 |
dubbo的整体架构围绕服务注册、服务发现、服务调用这三个维度来设计。
服务注册:服务提供者启动时,会向注册中心注册自己提供的服务,将服务信息、接口信息、权重信息等发布到注册中心(zookeeper或其它),然后等待服务调用请求的到来。
服务发现:服务调用者在启动时,会向注册中心订阅自己所需的服务,注册中心会将服务提供者列表返回给服务调用者。在这个使用过程中,注册中心会监听服务提供者的信息变化,若有变化会通知服务调用者刷新本地的配置缓存信息。
服务调用: 服务调用者在发业务请求时根据一系列负载均衡策略决定最终调用到哪一个服务提供者地址。在请求过程中,Dubbo支持多种通信协议和序列化方式。Dubbo客户端将请求序列化成二进制数据,并使用网络协议发送给服务提供者,服务提供者将调用请求数据反序列化之后,调用目标方法并将结果序列化成二进制数据返回给服务调用者。 在整个调用过程中,Dubbo会对服务调用进行监控,包括调用次数、异常次数、调用时长等等,以便于开发者对服务提供者和服务调用者服务进行故障排查和性能调优。
7、说说Dubbo的异步调用
dubbo的异步调用可以分为Provider端异步调用以及Consumer端异步调用。
Provider端:使用CompletableFuture 返回一个future对象。
Consumer端:使用CompletableFuture发起调用,然后future.get获取
8、说说dubbo的负载均衡策略
算法 | 描述 |
---|---|
加权随机 | 默认算法,默认权重相同,适合各个节点处理能力大致相等的情况 |
加权轮询 | 借鉴于Nginx的平滑加权轮询算法,默认权重相同,适合各个节点处理能力均匀且相对稳定的环境 |
最少活跃优先+加权随机 | 适合执行时间长短不一的服务调用,可以较好地处理突发请求,避免某一节点因长时间任务过多而响应缓慢 |
最短响应优先+加权随机 | 适合存在资源需求高且处理时间可能因负载而波动的应用 |
一致性Hash | 适合需要会话亲和性的场景,例如在一个用户的会话期间内需要由同一个服务实例来维护状态 |
9、Dubbo中的SPI机制
Dubbo 中的扩展能力是 JDK 标准的 SPI 扩展点机制的加强版。 两者的主要区别如下:
懒加载和预加载:JDK使用ServiceLoader.load()方法加载所有可用的服务方式,而Dubbo只有当实际需要使用某个服务时,才去加载服务的实现,这可以提高应用启动速递,并减少资源消耗。
配置文件格式和位置: JDK的SPI配置文件在META-INF/services目录下,文件名是完整的接口名称,文件内容是实现类的全限定名列表。Dubbo的SPI配置文件可以放在META-INF/dubbo或者其它目录,文件名也是完整的接口名称,但是文件内容可以包含键值对,提供了更丰富的配置选项和描述。

通过SPI思想,用户能够基于 Dubbo 提供的扩展能力,很方便基于自身需求扩展其他协议、过滤器、路由等。

Dubbo 扩展能力使得 Dubbo 项目很方便的切分成一个一个的子模块,实现热插拔特性。用户完全可以基于自身需求,替换 Dubbo 原生实现,来满足自身业务需求。
Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下。
10、如何设计一个RPC框架
可以从底层向上开始说起。
首先需要实现高性能的网络传输,可以采用 Netty 来实现,不用自己重复造轮子,然后需要自定义协议,毕竟远程交互都需要遵循一定的协议,然后还需要定义好序列化协议,网络的传输毕竟都是二进制流传输的。
然后可以搞一套描述服务的语言,即 IDL(Interface description language),让所有的服务都用 IDL 定义,再由框架转换为特定编程语言的接口,这样就能跨语言了。
此时最近基本的功能已经有了,但是只是最基础的,工业级的话首先得易用,所以框架需要把上述的细节对使用者进行屏蔽,让他们感觉不到本地调用和远程调用的区别,所以需要代理实现。
然后还需要实现集群功能,因此要服务发现、注册等功能,所以需要注册中心,当然细节还是需要屏蔽的。
最后还需要一个完善的监控机制,埋点上报调用情况等等,便于运维。
这样一个 RPC 框架的雏形就差不多了。
11、Dubbo的安全机制
Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。
Dubbo还提供服务黑白名单,来控制服务所允许的调用方。