Hadoop RPC:NameNode 和 DataNode 之间,是怎么”打电话”的?
Hadoop 是分布式系统,组件散落在不同节点上。
- NameNode 在 hadoop1 上
- DataNode 在 hadoop2、hadoop3 上
- ResourceManager 在 hadoop4 上
它们之间要通信——DataNode 要定期给 NameNode 发心跳、汇报块信息;Client 要向 NameNode 查文件位置。
它们怎么通信? 靠 RPC(远程过程调用)。
RPC 做的事:让你调用远程机器上的方法,像调用本地方法一样。
1 | // 本地调用:直接调 |
RPC 屏蔽了网络通信的细节——你不用管 Socket、不用管序列化、不用管连接管理,写代码的时候感觉像是在调本地方法。
四层架构概览:RPC 是怎么把本地调用”包装”成网络调用的
Hadoop RPC 采用分层设计,从下到上分为 序列化层、函数调用层、网络传输层 和 服务器端处理框架,每层专注于特定功能,共同支撑远程调用流程。
graph TD
A[应用层
远程方法调用] --> B[函数调用层
反射+动态代理]
B --> C[序列化层
对象-字节流转换]
C --> D[网络传输层
TCP/IP 通信]
D --> E[服务器端处理框架
Reactor 事件驱动]
note[四层协同 将本地方法调用转为跨节点通信]
| 层级 | 解决什么问题 | 关键技术 |
|---|---|---|
| 序列化层 | 对象怎么变成字节流传过去 | Writable 接口 |
| 函数调用层 | 远程机器怎么知道”调哪个方法” | 动态代理 + 反射 |
| 网络传输层 | 字节流怎么送到目标机器 | TCP 长连接 |
| 服务器处理层 | 服务器怎么高效处理大量请求 | Reactor 模型 + 线程池 |
逐层拆解:每一层在干什么
1. 序列化层:把对象变成字节流,收回来再还原
Java 有自带的 Serializable,但 Hadoop 没用——太重了,序列化结果里带类名、父类信息、字段签名,体积大、速度慢。
Hadoop 自己搞了一套 Writable 接口:
1 | public interface Writable { |
一个自定义类型要实现 Writable,手动定义”怎么写出、怎么读回”:
1 | public class UserInfo implements Writable { |
好处: 只存数据本身,不存类元数据,比 Java 原生序列化快、体积小。
2. 函数调用层:客户端”假装”在调本地方法,其实是远程
客户端拿到的不是真正的实现类,而是一个 动态代理对象。
1 | // 客户端拿到的是代理,不是真实对象 |
代理做的事情:
- 拦截方法调用 → 拿到方法名、参数类型、参数值
- 封装成 RPC 请求对象 → 交给序列化层
- 发出去 → 等响应 → 反序列化 → 返回结果
服务器端收到请求后,通过反射找到目标类和方法,执行,返回结果。
3. 网络传输层:字节流怎么送到目标机器
Hadoop RPC 基于 TCP 长连接:
- DataNode 启动时跟 NameNode 建立连接,保持住,不反复握手
- 每条 RPC 请求走这个连接发过去,响应从同一个连接回来
为什么用长连接? 短连接每次都要三次握手,心跳 3 秒一次,握手开销占比太高。
4. 服务器处理层:高并发请求怎么扛住
NameNode 要同时处理几百上千个 DataNode 的请求,每个请求还要做元数据操作。不能用”每个请求开一个线程”的简单模型——线程数爆炸。
Hadoop RPC 用 Reactor 模型:
- 一个 Reactor 线程 监听网络事件(新连接、数据可读)
- 数据到了,交给 Handler 线程池 处理(反序列化 → 反射调用 → 序列化响应)
Reactor 模型的好处: 一个线程处理所有 I/O 事件,真正耗时的业务逻辑交给线程池并发执行。既高效,又不阻塞 I/O。
完整调用流程:从 DataNode 到 NameNode 的心跳
以 DataNode 向 NameNode 发心跳为例:
sequenceDiagram
participant Client (DataNode)
participant Proxy (动态代理)
participant Server (NameNode)
participant Reactor (事件驱动)
participant Handler (请求处理)
Client->>Proxy: 调用 sendHeartbeat(参数)
Proxy->>Proxy: 拦截调用,封装请求信息
Proxy->>Proxy: 序列化参数为字节流
Proxy->>Server: 通过 TCP 发送请求字节流
Server->>Reactor: 监听并接收请求事件
Reactor->>Handler: 分发请求给线程池处理
Handler->>Handler: 反序列化字节流为请求对象
Handler->>Handler: 反射调用 NameNode 的 sendHeartbeat 方法
Handler->>Handler: 序列化返回结果为字节流
Server->>Proxy: 通过 TCP 返回响应字节流
Proxy->>Proxy: 反序列化响应为返回值
Proxy->>Client: 返回结果给客户端
整个流程,DataNode 的代码感觉只是调了一个方法。 RPC 把网络通信全包了。
Hadoop RPC 解决了什么问题
| 问题 | Hadoop RPC 怎么解决的 |
|---|---|
| 分布式节点要通信 | 封装成 RPC,调用远程像调用本地 |
| 通信要高效 | Writable 序列化 + TCP 长连接 + Reactor 模型 |
| 通信要可靠 | 基于 TCP + 重试机制 |
| 开发者不用管 Socket | 动态代理 + 反射,写接口就行 |
RPC 在 Hadoop 里用在哪
几乎所有的组件间通信都靠 RPC:
- NameNode ↔ DataNode:心跳、块报告、数据块操作
- ResourceManager ↔ NodeManager:资源汇报、容器启动
- Client ↔ NameNode:文件创建、块位置查询
- ApplicationMaster ↔ ResourceManager:资源申请
可以说,RPC 是 Hadoop 分布式系统的”神经系统”。 没有它,组件就是孤岛。