Dubbo如何实现像本地方法一样调用远程方法的?

典型回答 下图,是Dubbo的整体设计的一张图片,网上的很多资料都是基于这个官方给的图片展开的: 但是这样图片太复杂了,如果大家想要自己看源码,可以结合它来看,但是面试的时候回答问题,就不可能搞的这么细,所以主要还是介绍思想。(这里讲实现方式,具体的调用过程见:https://www.yuque.com/hollis666/ec96i7/nn5fo1yz2b2f9lgy ) 大家看着这样图,其实是有分层的。一共分成了以下几个层: 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实现远程调用的主要原理。 ...

March 22, 2026 · 1 min · santu

Dubbo支持哪些序列化方式?

典型回答 Dubbo是RPC框架,需要远程调用,那么就需要把请求和响应做序列化和反序列化。Dubbo目前支持多种序列化协议。 在Dubbo 3.0 中,内置了3种序列化协议: 比较常见的有: Hessian2:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式 Java:标准的Java序列化协议,易于使用但性能相对较低。 fastjson2:轻量级的数据交换格式,适用于简单的数据结构,易于阅读和调试。 这里面,hessian2是默认的,主要是因为他的性能是最好的,并且它支持跨语言。 而这几年,各种序列化框架层出不穷,今天出了一个号称速度快,明天出一个号称跨语言。总之有很多,于是在Dubbo的扩展中,就增加支持很多其他的序列化协议: 其中包括了avro、fastjson、fst、fury、gson、Jackson、kryo、msgpack、protobuf和protostuff等。 这里面很多框架其实性能都比较好,大有代替Hession的势头,比如Kryo、FST、fury等。 比如蚂蚁出的fury框架,号称比hessian快100倍,以下是他的一个整体介绍。详见:https://developer.aliyun.com/article/992485

March 22, 2026 · 1 min · santu

Dubbo支持哪些负载均衡策略?

典型回答 ✅什么是负载均衡,有哪些常见算法? Dubbo 支持多种负载均衡策略,允许服务消费者根据不同的场景和需求选择适合的策略。在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。 Dubbo 提供的是客户端负载均衡,即由 Consumer 通过负载均衡算法得出需要将请求提交到哪个 Provider 实例。 目前支持的负载均衡算法如下: 加权随机负载均衡(Random Load Balance) 随机负载均衡策略会从所有可用的提供者中随机选择一个。权重设置高的提供者被选中的概率更大,可以通过权重来调整每个服务提供者的负载。 适用场景:适用于各个服务提供者之间处理能力大致相等的情况。 轮询负载均衡(Round Robin Load Balance) 轮询负载均衡策略会按顺序逐一选择每个服务提供者。类似于随机负载均衡,它也支持权重设置。 适用场景:适合处理能力均匀且相对稳定的服务环境。 最少活跃调用数负载均衡(Least Active Load Balance) 这种策略优先调用当前活跃调用数最少的提供者。如果有多个提供者具有相同的活跃调用数,会随机选择一个。活跃调用数指的是某个提供者当前正在处理的调用数量。 适用场景:适用于执行时间长短不一的服务调用,可以较好地处理突发请求,避免某一节点因长时间任务过多而响应缓慢。 最短响应优先负载均衡(ShortestResponseLoadBalance) 最短响应优先负载均衡会跟踪每个服务提供者的最近响应时间,并优先选择那些响应时间最短的服务提供者。 适用场景:适用于存在资源需求高且处理时间可能因负载而波动的应用 一致性哈希负载均衡(Consistent Hash Load Balance) 一致性哈希负载均衡通过哈希算法将调用请求映射到某个服务提供者上,同一请求总是映射到相同的提供者。这种策略非常适合有状态的服务。 适用场景:非常适合需要会话亲和性(Session Affinity)的场景,例如,在一个用户的会话期内需要由同一个服务实例来维护状态。 扩展知识 配置方法 在 Dubbo 中,负载均衡策略可以通过配置来指定,既可以在服务提供方配置,也可以在消费方进行配置。例如,在消费方配置随机负载均衡可以通过 XML 或注解如下: 1 2 3 4 5 //服务端配置 <dubbo:service interface="com.example.SomeService" loadbalance="roundrobin"/> //客户端配置 <dubbo:reference interface="com.example.SomeService" loadbalance="roundrobin" /> 1 2 3 4 @Service(loadbalance = "random") public class SomeServiceImpl implements SomeService { // 实现方法 } 自定义负载均衡策略 Dubbo除了内置的负载均衡策略之外,还可以开发者自定义负载均衡策略。 ...

March 22, 2026 · 1 min · santu

Dubbo的整体架构是怎么样的?

典型回答 Dubbo的整体架构中,有多个角色,分别是服务提供者,服务调用者以及服务注册中心。一次完整的服务调用过程其实要分为服务注册、服务发现和服务调用三个过程。 1、服务注册:服务提供者在启动时,会向注册中心注册自己提供的服务,并将服务相关的信息(如服务名称、版本号、IP地址、端口号、协议、权重等)一并注册。Dubbo支持多种注册中心,包括ZooKeeper、Redis、Multicast、Simple等。一旦服务注册成功,服务提供者就可以等待服务调用请求的到来。 2、服务发现:服务调用者在启动时,需要向注册中心订阅自己所需的服务,注册中心会将服务提供者列表返回给服务调用者。当过程中,如果服务提供者列表发生变化,那么Dubbo会通知客户端进行变更。 3、服务调用:服务调用者需要根据负载均衡策略选择一个服务提供者,之后,就可以发送服务调用请求了。Dubbo支持多种通信协议和序列化方式。Dubbo客户端将调用请求序列化成二进制数据,并使用网络协议发送给服务提供者,服务提供者将调用请求反序列化后,调用目标方法并将结果序列化成二进制数据返回给服务调用者。在整个调用过程中 Dubbo会对服务调用进行监控,包括调用次数、调用时间、响应时间、异常次数、异常信息等,以便于服务提供者和服务调用者进行故障排查和性能调优。 扩展知识 连接方式 Dubbo提供了多种通信协议和通信方式,包括dubbo、http、hessian等,对于不同的通信方式,Dubbo也提供了不同的通信模型。对于服务提供者和服务消费者之间的通信,可以使用长连接模式或短连接模式,可以使用NIO或者Netty等高性能的通信框架,还可以设置心跳等机制来保持连接的稳定性。 对于服务提供者和注册中心之间的通信,Dubbo同样提供了多种注册中心实现,包括ZooKeeper、Redis、Multicast和Simple等,这些注册中心实现了不同的通信协议和通信方式。 在使用ZooKeeper作为注册中心时,Dubbo会通过ZooKeeper的长连接来注册服务和订阅服务提供者列表,并通过ZooKeeper提供的watch机制来监听服务提供者列表的变化。而在使用Redis作为注册中心时,则会使用Redis提供的订阅-发布机制来进行服务注册和发现。

March 22, 2026 · 1 min · santu

Dubbo的服务调用的过程是什么样的?

典型回答 下图,是Dubbo的官网中给出的一张关于Dubbo的完整调用链: 有些人能看懂,有些人看不懂,看不懂没关系,我给大家简化一下就可以看得懂了: 首先,一次调用从Proxy开始,这里主要是借助JDK、javaassist等实现的动态代理,然后在开始执行之前,会经过一个filter,判断一下本地是否存在接口的缓存以及mock等,如果都没有,开始通过invoker进行服务调用。 服务调用者在启动时会从注册中心拉取和订阅对应的服务列表, Cluster会把拉取的服务列表聚合成一个Invoker列表。 在Invoker执行过程中,会先通过Directory获取所有可以调用的服务提供者的列表。接下来在根据一些负载均衡的规则进行负载均衡来选出一个具体的Invoker。 在开始调用Invoker的invoke方法前,需要再进行一些过滤规则,如上下文处理、限流、以及计数等。 接下来就通过Invoker进行调用,在调用时会使用具体的客户端进行网络通信,如Netty,在这个过程中当然还需要做协议封装、数据的序列化等动作。 同时,客户端在收到请求后,同样需要进行反序列化,然后把请求交给线程池来进行调度执行。 再根据请求查找对应的Exporter,然后开始执行Invoker,这里就要调用具体的服务实现方法了。 然后再按照原路把结果返回。

March 22, 2026 · 1 min · santu

什么是RPC,和HTTP有什么区别?

典型回答 RPC 是Remote Procedure Call的缩写,译为远程过程调用。要想实现RPC通常需要包含传输协议和序列化协议的实现。 而我们熟知的HTTP,他的中文名叫超文本传输协议,所以他就是一种传输协议。所以,我们可以认为RPC和HTTP并不是同一个维度的两个概念。只不过他们都是可以作为远程调用的,所以经常拿来对比。 RPC的具体实现上,可以像HTTP一样,基于TCP协议来实现,也可以直接基于HTTP协议实现。 RPC主要用于公司内部服务之间的互相调用,所以他性能消耗低,传输效率高,服务治理方便。 而HTTP主要用于对外的异构环境,浏览器调用,APP接口调用,第三方接口调用等等。 ✅为什么RPC要比HTTP更快一些? 扩展知识 为什么需要远程调用 如今的大型网站都是分布式部署的。拿一个下单流程来说,可能涉及到物流、支付、库存、红包等多个系统后,而多个系统又是分别部署在不同的机器上的,分别由不同的团队负责。而要想实现下单流程,就需要用到远程调用。 1 2 3 4 5 6 下单{ 库存->减少库存 支付->扣款 红包->红包抵用 物流->生成物流信息 } 到底什么是远程过程调用 RPC 是指计算机 A 上的进程,调用另外一台计算机 B 上的进程,其中 A 上的调用进程被挂起,而 B 上的被调用进程开始执行,当值返回给 A 时,A 进程继续执行。调用方可以通过使用参数将信息传送给被调用方,而后可以通过传回的结果得到信息。而这一过程,对于开发人员来说是透明的。 就像后厨的例子一样,服务员把菜单传给后厨,厨师告诉备菜师和洗菜师开始工作,然后他等待他们完成工作。备菜师和洗菜师工作完之后,厨师开始炒菜。这个过程对于服务员来说其实是透明的,他不需要关心到底后厨是怎么做菜的。  由于各服务部署在不同机器上,要想在服务间进行远程调用免不了网络通信过程,服务消费方每调用一个服务都要写一坨网络通信相关的代码,不仅复杂而且极易出错。 如果有一种方式能让我们像调用本地服务一样调用远程服务,而让调用者对网络通信这些细节透明,那么将大大提高生产力,比如服务消费方在执行orderService.buy("HHKB键盘")时,实质上调用的是远端的服务。这种方式其实就是RPC。而提供了这种功能的工具我们称之为RPC框架。 在RPC框架中主要有三个角色:Provider、Consumer和Registry。如下图所示: Server: 暴露服务的服务提供方。 Client: 调用远程服务的服务消费方。 Registry: 服务注册与发现的注册中心。 服务提供方和服务消费方都比较好理解,就是后厨的洗菜师和厨师啦。厨师就是服务消费方,洗菜师就是服务提供方。厨师依赖洗菜师提供的服务。 服务注册中心又是个什么东西呢? 其实这个也比较好理解。对于那种很大的饭店来说,厨师可能有很多(集群部署),洗菜师也有很多(集群部署)。而厨师想要洗菜师帮忙洗菜的时候,他不会直接找某个洗菜师,而是通知一个中间人,这个人可能是洗菜师团队的领导,也可能就是一个专门协调后厨的人员。他知道整个厨房有多少洗菜师,也知道哪个洗菜师今天来上班了(需要先进行服务注册)。而且,他还可以根据各个洗菜师的忙碌情况动态分配任务(负载均衡)。 这个中间人就是服务注册中心。  服务提供者启动后主动向注册中心注册机器ip、port以及提供的服务列表; 服务消费者启动时向注册中心获取服务提供方地址列表,可实现软负载均衡和Failover; 实现RPC需要用到的技术 一个成熟的RPC框架需要考虑的问题有很多,这里只介绍实现一个远程调用需要用到的基本技术,感兴趣的朋友可以找一些开源的RPC框架代码来看下。 动态代理 生成 client stub和server stub需要用到 **Java 动态代理技术 **,我们可以使用JDK原生的动态代理机制,可以使用一些开源字节码工具框架 如:CgLib、Javassist等。 ...

March 22, 2026 · 1 min · santu

什么场景只能用HTTP,不能用RPC?

典型回答 1、在异构系统(跨语言和跨平台),HTTP具有更好的兼容性,因为HTTP是一种通用的协议,几乎所有的编程语言和操作系统都支持HTTP协议,而不是所有的编程语言和操作系统都支持相同的RPC协议。 2、RPC适合用在企业内部,要求使用同一套注册中心进行服务治理,如果是跨组织,或者跨公司,这种情况只能用更加通用的HTTP进行通信。 扩展知识 RPC有什么好处? 性能好:RPC在传输效率上通常比HTTP更高,此外,RPC可以使用更紧凑的数据格式,如Protocol Buffers和Thrift,可以更有效地利用网络带宽和存储空间。 安全性:目前,Dubbo等RPC框架主要应用在企业内部之间的系统调用,而内部系统之间调用的话安全性就更有保障一些。 调用简单:RPC可以帮我们像调用本地方法一样调用远程代码,而HTTP调用需要拼接Body、Header等等,过于复杂。

March 22, 2026 · 1 min · santu

Dubbo支持哪些调用协议?

典型回答 dubbo支持多种协议,主要有以下几个: 1、dubbo 协议 (默认) 默认就是走dubbo协议的,基于hessian作为序列化协议,单一长连接,TCP协议传输,NIO异步通信,适合大并发小数据量的服务调用,以及消费者远大于提供者,传输数据量很小(每次请求在100kb以内),但是并发量很高。 2、rmi 协议 采用JDK标准的rmi协议实现,传输参数和返回参数对象需要实现Serializable接口,使用java标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议TCP。 3、hessian 协议 集成Hessian服务,基于HTTP通讯,采用Servlet暴露服务,Dubbo内嵌Jetty作为服务器时默认实现,提供与Hession服务互操作。 hessian序列化协议,多个短连接,同步HTTP传输,传入参数较大,提供者大于消费者,提供者压力较大,适用于文件的传输,一般较少用; 4、http 协议 基于Http表单提交的远程调用协议,使用Spring的HttpInvoke实现。 5、webservice 协议 基于WebService的远程调用协议,集成CXF实现,提供和原生WebService的互操作。 6、thrift 协议 当前 dubbo 支持的 thrift 协议是对 thrift 原生协议 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。 7、memcached 协议 基于 memcached实现的 RPC 协议。 8、redis 协议 基于 Redis实现的 RPC 协议。 9、restful 基于标准的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的简写)实现的REST调用支持

March 22, 2026 · 1 min · santu

什么是Dubbo的优雅停机,怎么实现的?

典型回答 Dubbo是通过JDK的shutdown hook来完成优雅停机(下线)的。 对于服务提供方来说,应用下线时,先标记为不接受新请求,请求过来时直接报错,让客户端重试。然后,检测线程池中的线程是否正在运行,如果有,那就等线程执行完。 对于服务消费方来说,应用下线时,不再发起新的调用请求,所有的新调用在客户端侧直接报错。然后,检测还有没有远程请求没有得到返回的,如果有,则等待返回结果。 扩展知识 优雅上下线 关于"优雅上下线"这个词,我没找到官方的解释,我尝试解释一下这是什么。 首先,上线、下线大家一定都很清楚,比如我们一次应用发布过程中,就需要先将应用服务停掉,然后再把服务启动起来。这个过程就包含了一次下线和一次上线。 那么,“优雅"怎么理解呢? 先说什么情况我们认为不优雅: 1、服务停止时,没有关闭对应的监控,导致应用停止后发生大量报警。 2、应用停止时,没有通知外部调用方,很多请求还会过来,导致很多调用失败。 3、应用停止时,有线程正在执行中,执行了一半,JVM进程就被干掉了。 4、应用启动时,服务还没准备好,就开始对外提供服务,导致很多失败调用。 5、应用启动时,没有检查应用的健康状态,就开始对外提供服务,导致很多失败调用。 以上,都是我们认为的不优雅的情况,那么,反过来,优雅上下线就是一种避免上述情况发生的手段。 一个应用的优雅上下线涉及到的内容其实有很多,从底层的操作系统、容器层面,到编程语言、框架层面,再到应用架构层面,涉及到的知识很广泛。 其实,优雅上下线中,最重要的还是优雅下线。因为如果下线过程不优雅的话,就会发生很多调用失败了、服务找不到等问题。所以很多时候,大家也会提优雅停机这样的概念。 本文后面介绍的优雅上下线也重点关注优雅停机的过程。 操作系统&容器的优雅上下线 我们知道,kill -9之所以不建议使用,是因为kill -9特别强硬,系统会发出SIGKILL信号,他要求接收到该信号的程序应该立即结束运行,不能被阻塞或者忽略。 这个过程显然是不优雅的,因为应用立刻停止的话,就没办法做收尾动作。而更优雅的方式是kill -15。 当使用kill -15时,系统会发送一个SIGTERM的信号给对应的程序。当程序接收到该信号后,具体要如何处理是自己可以决定的。 kill -15会通知到应用程序,这就是操作系统对于优雅上下线的最基本的支持。 以前,在操作系统之上就是应用程序了,但是,自从容器化技术推出之后,在操作系统和应用程序之间,多了一个容器层,而Docker、k8s等容器其实也是支持优雅上下线的。 如Docker中同样提供了两个命令, docker stop 和 docker kill docker stop就像kill -15一样,他会向容器内的进程发送SIGTERM信号,在10S之后(可通过参数指定)再发送SIGKILL信号。 而docker kill就像kill -9,直接发送SIGKILL信号。 JVM的优雅上下线 在操作系统、容器等对优雅上下线有了基本的支持之后,在接收到docker stop、kill -15等命令后,会通知应用进程进行进程关闭。 而Java应用在运行时就是一个独立运行的进程,这个进程是如何关闭的呢? Java程序的终止运行是基于JVM的关闭实现的,JVM关闭方式分为正常关闭、强制关闭和异常关闭3种。 这其中,正常关闭就是支持优雅上下线的。正常关闭过程中,JVM可以做一些清理动作,比如删除临时文件。 当然,开发者也是可以自定义做一些额外的事情的,比如通知应用框架优雅上下线操作。 而这种机制是通过JDK中提供的shutdown hook实现的。JDK提供了Java.Runtime.addShutdownHook(Thread hook)方法,可以注册一个JVM关闭的钩子。 例子如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.hollis; public class ShutdownHookTest { public static void main(String[] args) { boolean flag = true; Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("hook execute..."); })); while (flag) { // app is runing } System.out.println("main thread execute end..."); } } 执行命令: ...

March 22, 2026 · 2 min · santu

什么是泛化调用?

典型回答 通常我们在使用RPC服务的时候,都是需要依赖别人提供的API的,这样我们就可以面向接口编程,就像调用本地代码一样直接调用接口方法就行了。在实际调用中,会通过代理的方式进行远程调用。 但是,有时候如果我们没有别人提供的API的话,怎么办呢,这时候就需要泛化调用了。 泛化调用是指在调用方没有服务方提供的API(SDK)的情况下,对服务方进行调用,并且可以拿到调用结果。 一般在测试集成工具、网关服务中用泛化调用比较多。 在Dubbo中,可以通过GenericService 调用所有服务实现: 1 2 3 4 5 <dubbo:reference id="testService" interface="com.hollis.TestService" generic="true" /> GenericService barService = (GenericService) applicationContext.getBean("testService"); Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" }); 通过使用GenericService服务即可实现对com.hollis.TestService服务实现泛化调用了,整个过程都不需要对应的API,只需要知道这个服务的全路径名就行了。

March 22, 2026 · 1 min · santu

留言给博主