Hadoop RPC流程

本文以Hadoop 2.8.0-RC1版本为基础,探讨Hadoop RPC的实现。首先介绍了如何导入和准备Hadoop源码,然后详细阐述了RPC的流程:服务端通过ServerSocketChannel监听端口,接收Socket连接后,Reader解析Request,根据协议类型调用相应Handler处理,Responsder返回RPC调用结果。客户端则通过获取服务元数据,使用RPC调用远程方法,实现数据的序列化和反序列化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近开始看Hadoop源码,本来想对照着《Hadoop技术内幕》看的,但是发现那本书对应的Hadoop版本太老了,还是基于1.x的,构建工具用的Ant,所以没有完全对照书看。

目前Hadoop最新的stable版本为2.8.0-RC1,本文以及之后的Hadoop源码相关的博文都以这个版本为基础。另外写的可能会糙一些。

首先把源码从GitHub上搞下来,查看一下都有哪些tag,并迁出最新的release (3.0.0-alpha版本就不考虑了)

git tag
git checkout release-2.8.0-RC1

Hadoop 2.x版本已经是采用maven构建了,在Intellij idea里面将Hadoop源码作为maven工程导入。

Hadoop是一个分布式系统,对它的学习我考虑首先从RPC机制开始。

打开 hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc ,发现这里面的类还真是多啊……需要选择一个合适的切入点才能开始看。

于是我将目光转向了 src/test/java/org/apache/hadoop/ipc 这个文件夹,里面是一些测试类,通过名字判断 TestPRC.java 是一个不错的切入点,点进去看了一下里面也确实是对Hadoop RPC完整流程的测试。于是开搞吧

如果想要运行测试的话,目前的代码是不行的,你可以看到很多文件都有报错,有一些类找不到。因为源码中只有Protobuf 的定义文件,但是没有生成具体的类。protoc 的安装流程这里不表述了,安装好Hadoop对应的 2.5版本的protoc之后,通过命令

mvn generate-test-sources

就可以生成需要的文件了。之后将target/generated-sources 以及target/generated-test-sources 中的文件复制到maintest 文件夹对应的路径中,就可以了

接下来简要的说一下总结的Hadoop RPC的流程

服务端

Server通过Listener启动Java NIO的ServerSocketChannel来监听指定的端口

public Listener() throws IOException {
  address = new InetSocketAddress(bindAddress, port);
  // Create a new server socket and set to non blocking mode
  acceptChannel = ServerSocketChannel.open();
  acceptChannel.configureBlocking(false);
  bind(acceptChannel.socket(), address, backlogLength, conf, portRangeConfig);

  ...
}

Server在接收到Socket连接请求后采用轮循的方式,将接入的Socket推入各个Reader的阻塞队列,并唤醒Reader的selector。由Reader对接入的Socket在selector上进行注册并处理

Reader负责解析Request

  • 如果Header是Http GET请求的话,就返回一条提示信息
  • 否则就从数据中解析RpcRequest,这里要根据RpcKind来决定用哪种RpcEngine,默认有BuildinWritableProtoBuffer三种
  • Reader会根据RpcRequest声称对应的RpcCall,并推入一个BlockingQueue中,等待调度

Request的数据结构:

  • 长度为4字节的 RPCHeader
    • 应该为常量hrpc
    • 如果为GET(GET后有空格)的话,说明收到的是HTTP GET请求,会返回一条提示信息
  • 长度为3字节的 connectionHeader
  • 长度为4字节的dataLength,为表示数据长度的int。
  • 长度为dataLength字节的datadata的数据结构::
    • Protobuf类型 RpcRequestHeaderProtoheader
      • header中会指明RpcKind
    • 根据header中的RpcKind,采用不同的request类型来解析后续的数据
  • 如果RpcKindprotobuf的话,那么后续的数据类型为RpcProtobufRequest,数据结构:
    • RequestHeaderProto
      • required string methodName = 1;
      • required string declaringClassProtocolName = 2;
      • required uint64 clientProtocolVersion = 3;
    • 自定义类型的message
  • 其中PRC的header RpcRequestHeaderProto 通过以下代码解析

    <T> T readFrom(ByteBuffer bb) throws IOException {
      // using the parser with a byte[]-backed coded input stream is the
      // most efficient way to deserialize a protobuf.  it has a direct
      // path to the PB ctor that doesn't create multi-layered streams
      // that internally buffer.
      CodedInputStream cis = CodedInputStream.newInstance(
          bb.array(), bb.position() + bb.arrayOffset(), bb.remaining());
      try {
        cis.pushLimit(cis.readRawVarint32());
        message = message.getParserForType().parseFrom(cis);
        cis.checkLastTagWas(0);
      } finally {
        // advance over the bytes read.
        bb.position(bb.position() + cis.getTotalBytesRead());
      }
      return (T) message;
    }
    

Handler负责处理RpcCall

  • 多个Handler并行从BlockingQueue中拉取RpcCall并执行
  • Handler根据protocol,method,参数,调用对应的invoker执行具体的操作
  • 执行结果会推入RpcCall对应的ConnectionRespond队列中,之后由Responder负责发送

Responsder负责返回RPC调用结果

客户端

  • 客户端通过RPC.getProxy来获得服务的动态代理
  • RPC.getProxy通过调用对应的RpcEnginegetProxy()方法来生成动态代理RpcEngine提供了对应的invoker
  • 对应的RpcEngine生成的动态代理主要提供数据的序列化
  • 远程调用通过Client类完成,每个SocketFactory只生成一个Client实例,Client会复用到不同RpcServer的Connection
  • Connection建立以后会自动启动独立的线程,排队发送Call,并等待返回结果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值