RPC 源码解析~Apache Dubbo

news/2025/1/16 11:31:04/

解析 RPC(远程过程调用)的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析,我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架,广泛应用于企业级应用中。

Dubbo的优劣势

优势
  1. 高性能:

    • Dubbo 使用 Netty 作为底层通信框架,支持高并发场景下的高效通信。
    • 支持多种序列化方式(如 Hessian2, Kryo, FST 等),可以根据需要选择最合适的序列化方案以提升性能。
  2. 丰富的功能:

    • 提供多种负载均衡策略(如 RandomLoadBalance, RoundRobinLoadBalance 等)。
    • 内置了服务降级、熔断和限流机制,增强了系统的健壮性。
    • 支持多协议(如 Dubbo 协议, HTTP 协议等),灵活适应不同的应用场景。
  3. 良好的生态系统:

    • 拥有成熟的社区支持和丰富的文档资源。
    • 可与 Spring Boot 和 Spring Cloud 等主流框架无缝集成,简化开发流程。
  4. 细粒度控制:

    • 提供详细的配置选项,允许开发者对每个服务进行精细化管理。
    • 支持服务分组、版本控制等功能,便于维护和升级。
  5. 强大的监控能力:

    • 内置了监控中心,可以实时查看服务的调用情况和性能指标。
    • 支持与其他监控系统(如 Prometheus, Grafana)集成,实现全面的监控解决方案。
劣势
  1. 依赖复杂:

    • Dubbo 依赖较多,引入时可能需要额外配置多个组件(如注册中心、序列化库等)。
    • 学习曲线相对陡峭,初学者需要一定时间掌握其复杂的配置和使用方法。
  2. 扩展性有限:

    • 尽管 Dubbo 提供了插件机制,但相对于一些现代微服务框架(如 Spring Cloud),其扩展性和灵活性稍显不足。
    • 对于某些特定需求,可能需要自行开发插件或中间件来满足。
  3. 生态整合难度:

    • 虽然可以与 Spring Boot 和 Spring Cloud 集成,但在某些高级特性上可能存在兼容性问题。
    • 相比 Spring Cloud 生态更为丰富和成熟,Dubbo 的第三方组件和支持程度略逊一筹。
  4. 社区活跃度:

    • 虽然 Dubbo 社区活跃,但由于近年来 Spring Cloud 的崛起,部分开发者更倾向于使用后者。
    • 新的功能更新速度相对较慢,不如 Spring Cloud 快速迭代。

解析目标

我们将逐步解析 Dubbo 的核心组件和流程,包括但不限于以下部分:

  1. 服务注册与发现:如何将服务注册到注册中心,并从注册中心发现可用的服务。
  2. 代理机制:客户端和服务端如何通过动态代理来透明地进行远程调用。
  3. 序列化:数据在网络传输过程中如何被序列化和反序列化。
  4. 负载均衡:客户端如何根据不同的策略选择合适的服务提供者。
  5. 通信协议:底层网络通信是如何实现的,包括 Netty 等网络框架的应用。

准备工作

在开始解析之前,请确保你已经具备以下条件:

  1. Java 开发环境:安装 JDK 8 或更高版本。
  2. Git 工具:用于克隆 Dubbo 源码仓库。
  3. IDE:推荐使用 IntelliJ IDEA 或 Eclipse。
  4. Maven:用于构建和管理依赖。

克隆 Dubbo 源码

首先,克隆 Dubbo 的 GitHub 仓库到本地:

bash

git clone https://github.com/apache/dubbo.git
cd dubbo

构建 Dubbo 源码

使用 Maven 构建 Dubbo 源码:

bash

mvn clean install -DskipTests=true

核心组件解析

1. 服务注册与发现

Dubbo 使用 ZooKeeper 作为默认的注册中心。以下是服务注册和发现的核心流程。

服务注册

当服务提供者启动时,会将其元数据注册到注册中心。主要涉及 RegistryProtocolZookeeperRegistry 类。

  • RegistryProtocol: 负责处理服务注册和订阅逻辑。
  • ZookeeperRegistry: 实现了具体的注册中心操作,如连接 ZooKeeper 并执行注册。

关键代码位置:

  • dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/RegistryProtocol.java
  • dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java

核心方法:

  • export(Invoker<T> invoker): 导出服务并注册到注册中心。
  • doRegister(URL url): 执行实际的注册操作。

示例代码:

java

@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {URL registryUrl = getRegistryUrl(originInvoker);// 创建注册器实例Registry registry = getRegistry(registryUrl);final URL providerUrl = getProviderUrl(originInvoker, registryUrl);// 向注册中心注册服务final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);ProviderModel providerModel = new ProviderModel(providerUrl.getServiceKey(), originInvoker, providerUrl);ApplicationModel.registerProvider(providerModel);registry.register(registeredProviderUrl);// 订阅 override 数据registry.subscribe(getSubscribedOverrideUrl(providerUrl), event -> {if (logger.isDebugEnabled()) {logger.debug("Notify urls for subscribe url " + event.getUrl() + ", urls: " + event.getUrls());}Map<String, String> notifiedUrls = new HashMap<>();if (CollectionUtils.isNotEmpty(event.getUrls())) {event.getUrls().forEach(url -> notifiedUrls.put(url.getServiceKey(), url.toFullString()));}refreshOverrideAndInvoker(providerUrl, notifiedUrls);});exporterMapLock.lock();try {ExporterChangeableWrapper<T> exporter = new ExporterChangeableWrapper<>(originInvoker, null);exporters.put(originInvoker, exporter);return exporter;} finally {exporterMapLock.unlock();}
}
服务发现

当服务消费者启动时,会从注册中心订阅服务提供者的地址列表。主要涉及 RegistryProtocolZookeeperRegistry 类。

  • RegistryProtocol: 处理订阅逻辑,并生成服务代理。
  • ZookeeperRegistry: 监听注册中心的变化,并通知消费者。

关键代码位置:

  • dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/RegistryProtocol.java
  • dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java

核心方法:

  • refer(Class<T> type, URL url): 引用远程服务并创建代理对象。
  • doSubscribe(URL url, NotifyListener listener): 执行实际的订阅操作。

示例代码:

java

@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, mapLocalHost(url.getParameter(Constants.REGISTER_IP_KEY, NetUtils.getLocalHost())), 0,type.getName(), url.getParameters());// 设置检查是否延迟暴露if (!Constants.YES_VALUE.equals(url.getParameter(Constants.LAZY_CONNECT_KEY))) {checkWhetherMetadataCenterExist(subscribeUrl);List<URL> urls = registryCache.get(subscribeUrl);if (urls != null && !urls.isEmpty()) {// 如果存在直接返回StaticDirectory<T> directory = new StaticDirectory<>(subscribeUrl, toInvokers(urls));doRefer(subscribeUrl, directory);return cluster.join(directory);}}// 创建动态目录DynamicDirectory<T> directory = new DynamicDirectory<>(subscribeUrl, registry, directoryFactory, false);directory.buildRouterChain(subscribeUrl);directory.setConsumerUrl(subscribeUrl);directoryList.add(directory);registry.subscribe(subscribeUrl, new CacheListener(directory, url));// 创建集群Invoker<T> clusterInvoker = cluster.join(directory);providersModel.setClusterInvoker(clusterInvoker);return clusterInvoker;
}
2. 代理机制

Dubbo 使用动态代理来简化远程调用的过程。主要涉及 ProxyFactoryJavassistProxyFactory 类。

  • ProxyFactory: 定义了代理工厂接口。
  • JavassistProxyFactory: 实现了具体的代理创建逻辑。

关键代码位置:

  • dubbo-common/src/main/java/org/apache/dubbo/common/proxy/ProxyFactory.java
  • dubbo-proxy/dubbo-proxy-javassist/src/main/java/org/apache/dubbo/common/proxy/javassist/JavassistProxyFactory.java

核心方法:

  • getProxy(Invoker<?> invoker): 获取服务代理对象。
  • getInvoker(T proxy, Class<T> type, URL url): 将代理对象转换为 Invoker。

示例代码:

java

@Override
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {// TODO Wrapper cannot handle this scenario at this moment.throw new UnsupportedOperationException("Not supported.");
}
3. 序列化

Dubbo 支持多种序列化方式,如 Hessian2, Kryo, FST 等。主要涉及 Serialization 和具体序列化类。

  • Serialization: 定义了序列化接口。
  • Hessian2Serialization: 实现了 Hessian2 序列化的具体逻辑。

关键代码位置:

  • dubbo-common/src/main/java/org/apache/dubbo/common/serialize/Serialization.java
  • dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2Serialization.java

核心方法:

  • serialize(URL url, OutputStream output): 返回序列化对象。
  • deserialize(URL url, InputStream input): 返回反序列化对象。

示例代码:

java

@Override
public ObjectOutput serialize(URL url, OutputStream out) throws IOException {Hessian2ObjectOutputStream hessian2os = new Hessian2ObjectOutputStream(out);com.caucho.hessian.io.SerializerFactory serializerFactory = getSerializerFactory(url);if (serializerFactory != null) {hessian2os.setSerializerFactory(serializerFactory);}return new CompatibleObjectOutput(hessian2os);
}@Override
public ObjectInput deserialize(URL url, InputStream is) throws IOException {Hessian2ObjectInputStream hessian2is = new Hessian2ObjectInputStream(is);com.caucho.hessian.io.DeserializerFactory deserializerFactory = getDeserializerFactory(url);if (deserializerFactory != null) {hessian2is.setDeserializerFactory(deserializerFactory);}return new CompatibleObjectInput(hessian2is);
}
4. 负载均衡

Dubbo 提供多种负载均衡策略,如 RandomLoadBalance, RoundRobinLoadBalance 等。主要涉及 LoadBalance 和具体策略类。

  • LoadBalance: 定义了负载均衡接口。
  • RandomLoadBalance: 实现了随机负载均衡的具体逻辑。

关键代码位置:

  • dubbo-cluster/src/main/java/org/apache/dubbo/rpc/loadbalance/LoadBalance.java
  • dubbo-cluster/src/main/java/org/apache/dubbo/rpc/loadbalance/RandomLoadBalance.java

核心方法:

  • select(List<Invoker<T>> invokers, URL url, Invocation invocation): 选择合适的 Invoker。

示例代码:

java

@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {int length = invokers.size(); // Number of providersint totalWeight = 0; // The sum of weightsboolean sameWeight = true; // Every provider has the same weight?for (int i = 0; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);totalWeight += weight; // Sumif (sameWeight && i > 0 && weight != getWeight(invokers.get(i - 1), invocation)) {sameWeight = false;}}if (totalWeight > 0 && !sameWeight) {// If (not every provider has the same weight & at least one provider's weight>0), select randomly based on totalWeight.int offset = ThreadLocalRandom.current().nextInt(totalWeight);// Return a invoker based on the random value.for (int i = 0; i < length; i++) {Invoker<T> invoker = invokers.get(i);offset -= getWeight(invoker, invocation);if (offset < 0) {return invoker;}}}// If all providers have the same weight value or totalWeight=0, return evenly.return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
5. 通信协议

Dubbo 支持多种通信协议,如 Dubbo 协议, HTTP 协议等。主要涉及 Transporter 和具体协议类。

  • Transporter: 定义了通信接口。
  • NettyTransporter: 实现了基于 Netty 的通信逻辑。

关键代码位置:

  • dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporter.java
  • dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporter.java

核心方法:

  • bind(URL url, ChannelHandler handler): 绑定服务器端口。
  • connect(URL url, ChannelHandler handler): 连接到服务器。

示例代码:

@Override
public Server bind(URL url, ChannelHandler handler) throws RemotingException {return new NettyServer(url, handler);
}@Override
public Client connect(URL url, ChannelHandler handler) throws RemotingException {return new NettyClient(url, wrapChannelHandler(url, handler));
}

示例项目

为了更好地理解上述解析内容,我们可以创建一个简单的 Dubbo 示例项目,包含服务提供者和消费者。

1. 创建服务接口

定义一个简单的服务接口:

UserApi.java:

java

package com.example.dubbo.service;public interface UserApi {String sayHello(String name);
}
2. 实现服务提供者

实现服务接口并启动服务提供者:

UserServiceImpl.java:

java

package com.example.dubbo.provider;import com.example.dubbo.service.UserApi;
import org.apache.dubbo.config.annotation.DubboService;@DubboService(version = "1.0.0")
public class UserServiceImpl implements UserApi {@Overridepublic String sayHello(String name) {return "Hello, " + name;}
}

ProviderApplication.java:

java

package com.example.dubbo.provider;import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.example.dubbo.provider")
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}
}

application.properties:

# Dubbo Application Info
dubbo.application.name=user-provider
dubbo.registry.address=zookeeper://localhost:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
3. 实现服务消费者

创建服务消费者并调用远程服务:

ConsumerApplication.java:

java

package com.example.dubbo.consumer;import com.example.dubbo.service.UserApi;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class ConsumerApplication implements CommandLineRunner {@DubboReference(version = "1.0.0")private UserApi userApi;public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}@Overridepublic void run(String... args) throws Exception {String result = userApi.sayHello("World");System.out.println(result); // Output: Hello, World}
}

application.properties:

# Dubbo Application Info
dubbo.application.name=user-consumer
dubbo.registry.address=zookeeper://localhost:2181

运行示例

  1. 启动 ZooKeeper: 确保 ZooKeeper 服务正在运行。如果没有安装,可以从 ZooKeeper 官网 下载并按照官方文档进行安装和启动。

  2. 启动服务提供者

bash

cd provider
mvn spring-boot:run

3.启动服务消费者

bash

cd consumer
mvn spring-boot:run

总结

通过对 Dubbo 源码的解析,我们深入了解了 RPC 框架的关键组成部分和工作原理,包括服务注册与发现、代理机制、序列化、负载均衡和通信协议。此外,我们还创建了一个简单的示例项目,展示了如何使用 Dubbo 进行服务开发和部署。


http://www.ppmy.cn/news/1563585.html

相关文章

(即插即用模块-Attention部分) 四十四、(ICIP 2022) HWA 半小波注意力

文章目录 1、Half Wavelet Attention2、代码实现 paper&#xff1a;HALFWAVELET ATTENTION ON M-NET FOR LOW-LIGHT IMAGE ENHANCEMENT Code&#xff1a;https://github.com/FanChiMao/HWMNet 1、Half Wavelet Attention 传统的图像增强方法主要关注图像在空间域的特征信息&am…

【Linux】从零开始:编写你的第一个Linux进度条小程序

Linux相关知识点可以通过点击以下链接进行学习一起加油&#xff01;初识指令指令进阶权限管理yum包管理与vim编辑器GCC/G编译器make与Makefile自动化构建GDB调试器与Git版本控制工具 文章目录 一、知识铺垫1.1 回车与换行概念1.2 缓冲区 二、实现简单倒计时三、进度条3.1 Verrs…

十五、从0开始卷出一个新项目之瑞萨RZN2L之PHID例程

一、概述 RZN2L未提供PHID的例程&#xff0c;这里提供来逻辑PHID和RTOS PHID例程 USB知识&#xff1a;嵌入式科普(24)一张图真正看懂USB通信协议 二、README.txt 1.接电源线、调试线、串口线、usb线到pc 2、xspi模式运行&#xff0c;启动后串口输出日志&#xff0c;HID收发…

Python----Python高级(函数基础,形参和实参,参数传递,全局变量和局部变量,匿名函数,递归函数,eval()函数,LEGB规则)

一、函数基础 1.1、函数的用法和底层分析 函数是可重用的程序代码块。 函数的作用&#xff0c;不仅可以实现代码的复用&#xff0c;更能实现代码的一致性。一致性指的是&#xff0c;只要修改函数的代码&#xff0c;则所有调用该函数的地方都能得到体现。 在编写函数时&#xf…

了解npm:JavaScript包管理工具

在JavaScript的生态系统中&#xff0c;npm&#xff08;Node Package Manager&#xff09;无疑是一个举足轻重的存在。它不仅是Node.js的包管理器&#xff0c;更是前端开发不可或缺的一部分&#xff0c;为开发者提供了丰富的包资源、便捷的包管理以及强大的社区支持。本文将深入…

EMS专题 | 守护数据安全:数据中心和服务器机房环境温湿度监测

您需要服务器机房温度监测解决方案吗&#xff1f; 服务器机房是企业中用于存储、管理和维护服务器及其相关组件的设施。服务器机房通常位于数据中心内&#xff0c;是一个专门设计的物理环境&#xff0c;旨在确保服务器的稳定运行和数据的安全性。服务器机房主要起到存储和管理数…

ES6的高阶语法特性

一、模板字符串的高级用法 1.1.模板字符串的嵌套 模板字符串的嵌套允许在一个模板字符串内部再嵌入一个或多个模板字符串。这种嵌套结构在处理复杂数据结构或生成具有层级关系的文本时非常有用。 1. 嵌套示例 假设我们有一个包含多个对象的数组&#xff0c;每个对象都有名称、…

ElasticSearch常见知识点

1、什么是ElasticSearch&#xff1f; Elasticsearch 是基于 Lucene 的 Restful 的分布式实时全文搜索引擎&#xff0c;每个字段都被索引并可被搜索&#xff0c;可以快速存储、搜索、分析海量的数据。 2、什么是倒排索引&#xff1f; 正常的索引是比如二叉树。倒排索引是用内容…