RPC 源码解析~Apache Dubbo

解析 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.mzph.cn/pingmian/67201.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos7.6 安装nginx 1.21.3与配置ssl

1 安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel2 下载Nginx wget http://nginx.org/download/nginx-1.21.3.tar.gz3 安装目录 mkdir -p /data/apps/nginx4 安装 4.1 创建用户 创建用户nginx使用的nginx用户。 #添加www组 # groupa…

高级软件工程-复习

高级软件工程复习 坐标国科大&#xff0c;下面是老师说的考试重点。 Ruby编程语言的一些特征需要了解要能读得懂Ruby程序Git的基本命令操作知道Rails的MVC工作机理需要清楚&#xff0c;Model, Controller, View各司什么职责明白BDD的User Story需要会写&#xff0c;SMART要求能…

TrollFools 2.10-22 插件注入工具 官方版

《TrollFools巨魔设备专用插件注入工具》这是一款专为巨魔设备打造的插件注入神器&#xff0c;功能强大且操作便捷。它能够轻松地将插件注入通过AppStore商店下载的任意APP中&#xff0c;同时也能随时卸载&#xff0c;丝毫不影响APP的正常使用。注入后的APP仍可正常更新&#x…

30分钟内搭建一个全能轻量级springboot 3.4 + 脚手架 <1> 5分钟快速创建一个springboot web项目

快速导航 <1> 5分钟快速创建一个springboot web项目 <2> 5分钟集成好最新版本的开源swagger ui&#xff0c;并使用ui操作调用接口 <3> 5分钟集成好druid并使用druid自带监控工具监控sql请求 <4> 5分钟集成好mybatisplus并使用mybatisplus generator自…

软考信安20~数据库系统安全

1、数据库安全概况 1.1、数据库安全概念 数据库是网络信息系统的基础性软件,承载着各种各样的数据,成为应用系统的支撑平台。 国外主流的数据库系统有MSSQL 、MySQL 、Oracle 、DB2 等,国产数据库系统主要有人大金仓、达梦等。 1.2、数据库安全威胁 授权的误用(Misuses…

Python脚本自动发送电子邮件

要编写一个Python脚本来自动发送电子邮件&#xff0c;你可以使用smtplib库来处理SMTP协议&#xff0c;以及email库来构建邮件内容。 安装必要的库 通常情况下&#xff0c;smtplib和email库是Python标准库的一部分&#xff0c;因此不需要额外安装。如果你使用的是较旧的Python版…

按键精灵ios越狱脚本教程:多选框联动的ui界面

以下是一个简单的 iOS 代码示例&#xff0c;使用 Swift 语言来创建一个包含多选框&#xff08;复选框&#xff09;的 UI 界面&#xff0c;并实现联动效果。 import UIKitclass ViewController: UIViewController {let checkbox1 UIButton(type:.system)let checkbox2 UIButt…

arcgis中生成格网矢量带高度

效果 1、数据准备 (1)矢量边界(miain.shp) (2)DEM(用于提取格网标高) (3)DSM(用于提取格网最高点) 2、根据矢量范围生成格网 模板范围选择矢量边界,像元宽度和高度根据坐标系来输入,我这边是4326的,所以输入的是弧度,输出格网矢量gewang.shp 3、分区统计 …

ubuntu20升级至22后不兼容ssh-rsa加密算法

背景 升级ubuntu20后22后&#xff0c;远程连接工具无法通过public key方式登录主机。 分析&解决 查看sshd服务有报错journatl -xeu sshd 1月 10 19:10:01 k8s-master03 sshd[21093]: userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedAlgorithms [preauth] 1月 …

C#的Task

优先使用Task.Run&#xff0c;除非有定制化需求才用Task.Factory.StartNew Task.Factory.StartNew的TaskScheduler参数颠覆你的认知&#xff1a; var cnt 0;var cancelToken new CancellationTokenSource();await Task.Factory.StartNew(() > {cnt;Debug.WriteLine($&quo…

JavaScript 实现继承的详细方法:深入探讨

文章目录 前言一、原型链 (Prototype Chain)二、借用构造函数 (Constructor Stealing / Classical Inheritance)三、组合式继承 (Combination Inheritance)四、原型式继承 (Prototypal Inheritance)五、寄生式继承 (Parasitic Inheritance)六、ES6 类 (Class) 和 extends 关键字…

海豚调度DolphinScheduler-3.1.9配置windows本地开发环境

源代码下载地址https://dolphinscheduler.apache.org/zh-cn/docs/3.1.9 1.Zookeeper安装与使用 如图下载解压zookeeper安装包&#xff0c;并创建data和log目录 下载地址 https://archive.apache.org/dist/zookeeper/zookeeper-3.6.4/apache-zookeeper-3.6.4-bin.tar.gz 进入…

P1图文解析:初识算法和数据结构

文章目录 前言1、算法例子1.1、查字典&#xff08;二分查找算法&#xff09;1.2、整理扑克&#xff08;插入排序算法&#xff09;1.3、货币找零&#xff08;贪心算法&#xff09; 2、算法与数据结构2.1、算法定义2.2、数据结构定义2.3、数据结构与算法的关系2.4、独立于编程语言…

校园跑腿小程序---轮播图,导航栏开发

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

C# 中对 Task 中的异常进行捕获

以下是在 C# 中对 Task 中的异常进行捕获的几种常见方法&#xff1a; 方法一&#xff1a;使用 try-catch 语句 你可以使用 try-catch 语句来捕获 Task 中的异常&#xff0c;尤其是当你使用 await 关键字等待任务完成时。 using System; using System.Threading.Tasks;class …

UE材质节点Fresnel

Fresnel节点 ExponentIn 控制边缘透明度 BaseReflectFractionIn 控制中心透明度

大数据学习(34)-mapreduce详解

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…

浅谈云计算07 | 云安全机制

浅谈云计算安全机制&#xff1a;全方位守护云端世界 一、引言二、加密技术&#xff1a;数据的隐形护盾三、散列机制&#xff1a;数据完整性的忠诚卫士四、数字签名&#xff1a;数据来源与真伪的鉴定专家五、公钥基础设施&#xff08;PKI&#xff09;&#xff1a;信任的基石六、…

docker-compose部署kafka 3.3.1 kraft

一、服务器&#xff1a; 节点1:10.1.1.165 节点2:10.1.1.164 节点3:10.1.1.169二、添加环境地址解析 vim /etc/hosts kafka1 10.1.1.165 kafka2 10.1.1.164 kafka3 10.1.1.169三、节点配置 节点1 version: "3" services:kafka1:image: bitnami/kafka:3.3.1contain…

Notepad++上NppFTP插件的安装和使用教程

一、NppFTP插件下载 图示是已经安装好了插件。 在搜索框里面搜NppFTP&#xff0c;一般情况下&#xff0c;自带的下载地址容易下载失败。这里准备了一个下载连接&#xff1a;Release v0.29.10 ashkulz/NppFTP GitHub 这里我下载的是x86版本 下载好后在nodepad的插件里面选择打…