Dubbo 的 服务暴露(Export) 和 服务引用(Refer) 是其核心流程,分别对应服务提供者(Provider)暴露服务 和 服务消费者(Consumer)引用远程服务的过程。整个过程高度依赖 Dubbo 的 SPI 机制、代理、注册中心和网络通信模块。
一、总体流程概览
Provider(服务提供者) Consumer(服务消费者)
| |
| 1. 服务暴露(Service Export) | 2. 服务引用(Service Refer)
| → 本地暴露(JVM 内调用优化) | → 创建代理对象
| → 注册到注册中心 | → 从注册中心订阅服务
| → 启动网络服务器(Netty)监听调用 | → 接收服务变更通知
| |
|<----------------------------------------|
| 3. 远程调用(RPC) |
|---------------------------------------->|
二、服务暴露(Service Export)过程(Provider 端)
当 Provider 启动时,会将实现的服务接口暴露出去,供 Consumer 调用。
1. 配置解析
- 通过 XML、注解(
@DubboService)或 Java 配置加载服务信息:@DubboService public class UserServiceImpl implements UserService { ... } - 解析出:接口名、实现类、版本、分组、超时、权重、协议等。
2. 创建 Invoker
- Dubbo 抽象了
Invoker接口,代表一个可执行的远程服务。 - 通过代理工厂(ProxyFactory)为服务实现类创建
AbstractProxyInvoker。Invoker<UserService> invoker = proxyFactory.getInvoker(serviceImpl, UserService.class, url);
3. 协议暴露(Protocol.export)
- 根据配置的协议(如
dubbo、http、gRPC)进行暴露。 - 调用
Protocol扩展点的export()方法:Exporter<UserService> exporter = protocol.export(invoker); - Dubbo 协议核心逻辑:
- 启动 Netty 服务器(默认端口 20880)
- 绑定
NettyServer,监听请求 - 建立连接、处理编解码(如 Hessian2)、反序列化调用请求
4. 注册到注册中心
- 将服务元信息(URL)注册到注册中心(如 Nacos、ZooKeeper):
dubbo://192.168.1.100:20880/com.example.UserService? version=1.0.0& group=user& timeout=5000& weight=100 - 注册中心保存:服务名 → 多个 Provider 地址列表
5. 本地暴露(JVM 优化,可选)
- 同时暴露一个本地 JVM 协议(
injvm://),用于同一进程内的调用优化,避免网络开销。
三、服务引用(Service Refer)过程(Consumer 端)
Consumer 在启动时,会“引用”远程服务,生成一个本地代理对象,调用时透明发起远程调用。
1. 配置解析
- 通过
@DubboReference或 XML 配置声明引用的服务:@DubboReference private UserService userService;
2. 从注册中心订阅服务
- 向注册中心发送订阅请求:
subscribe: com.example.UserService:1.0.0 - 注册中心返回当前可用的 Provider 列表(IP:Port + URL 参数)
- Consumer 在本地缓存服务提供者列表
3. 监听服务变更
- 注册监听器,监听 Provider 的上线/下线事件
- 当 Provider 变化时,更新本地的 Invoker 列表(如新增、删除节点)
4. 创建 Invoker
- 对每个 Provider 创建一个
DubboInvoker - 封装网络通信逻辑(Netty 客户端)
- 所有 Invoker 被包装成一个
Directory(目录服务)
5. 集群容错包装(Cluster)
- 通过
Cluster扩展(如FailoverCluster)将多个 Invoker 包装成一个ClusterInvoker - 负责负载均衡、失败重试等逻辑
6. 生成代理对象(Proxy)
-
使用代理工厂(JDK 动态代理、Javassist、ByteBuddy)生成代理对象
-
当调用
userService.getUser(1)时,实际调用的是代理逻辑public class $Proxy0 implements UserService { public User getUser(Long id) { // 被拦截,走远程调用逻辑 return clusterInvoker.invoke(new RpcInvocation("getUser", ...)); } }
四、关键组件协作图
[Provider]
|
↓
ServiceConfig → Invoker → Protocol.export → NettyServer (监听)
↓
RegistryProtocol → 注册到 ZooKeeper/Nacos
[Consumer]
|
↓
ReferenceConfig → RegistryProtocol.subscribe → 从注册中心获取 Provider 列表
↓
Directory → 多个 Invoker
↓
Cluster → ClusterInvoker(负载均衡 + 容错)
↓
ProxyFactory → 生成代理对象(proxy)
↓
userService → 透明调用
五、服务暴露与引用的核心类
| 类名 | 作用 |
|---|---|
ServiceConfig | Provider 端,控制服务暴露流程 |
ReferenceConfig | Consumer 端,控制服务引用流程 |
ProxyFactory | 生成代理对象(SPI 扩展) |
Protocol | 协议层,控制服务暴露和引用(SPI) |
RegistryProtocol | 负责注册/订阅逻辑 |
Invoker | 远程调用的抽象 |
Exporter | 暴露后的引用,用于取消暴露 |
Cluster | 集群容错,包装多个 Invoker |
LoadBalance | 从多个 Invoker 中选择一个 |
六、一次远程调用的完整流程(暴露 + 引用之后)
- Consumer 调用
userService.getUser(1) - 代理对象拦截,封装为
RpcInvocation ClusterInvoker从Directory获取可用Invoker列表LoadBalance选择一个Invoker(如随机)DubboInvoker通过 Netty 发送请求(序列化:Hessian2)- Provider 端
NettyServer接收请求,反序列化 - 根据接口名、方法名反射调用本地实现
- 返回结果,反向传递给 Consumer
- Consumer 反序列化结果,返回给调用方
七、高级特性支持
| 特性 | 实现方式 |
|---|---|
| 延迟暴露 | delay="5000",延迟 5 秒暴露 |
| 多协议暴露 | <dubbo:service protocol="dubbo,http"/> |
| 多注册中心 | 支持同时注册到 Nacos 和 ZooKeeper |
| 本地存根(Stub) | 自定义本地逻辑预处理 |
| 异步调用 | 支持 CompletableFuture |
| 泛化调用 | 不依赖接口,直接调用 GenericService |
八、总结
| 阶段 | 关键动作 | 核心目标 |
|---|---|---|
| 服务暴露 | 创建 Invoker → 启动 Netty → 注册到注册中心 | 让服务可被发现和调用 |
| 服务引用 | 订阅服务 → 创建 Invoker 列表 → 生成代理 | 让调用像本地方法一样透明 |
| 远程调用 | 代理拦截 → 负载均衡 → 网络通信 → 反射执行 | 实现高性能 RPC |
✅ Dubbo 的服务暴露和引用,本质是:
- 暴露:把本地服务变成可远程调用的网络服务
- 引用:把远程服务变成可本地调用的代理对象
通过 注册中心 + 代理 + Invoker 抽象 + 协议扩展 实现了 透明化远程调用。
📌 建议学习路径:
- 阅读
ServiceConfig.export()和ReferenceConfig.get()源码 - 调试一次 Dubbo 启动过程
- 查看
DubboProtocol和RegistryProtocol实现 - 理解
Invoker和Exporter的抽象意义
428

被折叠的 条评论
为什么被折叠?



