Hadoop 源码解析-rpc扩展

本文详细介绍了如何在Hadoop中扩展RPC机制,特别是在Datanode与Namenode之间增加新的通讯接口。通过修改DatanodeProtocol.java和DatanodeProtocol.proto文件,并在相关类中实现新接口,成功实现了Datanode向Namenode注册的功能。

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

Hadoop 源码解析-rpc修改

最近因为实验室的项目需求,需要修改Hadoop,在Datanode与Namenode之间增加一个通讯接口以供项目调用。看过一段时间的源码之后,对自己看过的部分做一个总结,以下说法不一定正确,如果有错误还请大家指正。

Hadoop现在默认使用google的protobuf来完成序列化的工作,且Hadoop的RPC机制也是基于Protobuf实现的。那么我们要在Hadoop中加一个RPC的调用来实现Datanode与Namenode之间的信息传递或者其他的目的的时候应该怎么做?
Hadoop的最主要的几个通讯协议是ClientProtocol,NamenodeProtocol,ClientDatanodeProtocol,DatanodeProtocol协议,各协议的用途与通讯的两端的对象如下图与表所示:
这里写图片描述

协议名作用
ClientDatanodeProtocolClient与Datanode之间的通讯协议接口
ClientProtocolClient与Namenode之间的交互的接口(创建文件,删除文件等操作)
NamenodeProtocolSecondaryNamenode和Namenode之间的通讯的接口
DatanodeProtocolDatanode与Namenode之间的通讯的接口(心跳,Blockreport等)

从上面的图表可以看出如果我们要在Datanode与Namenode之间增加一个接口供我们使用那么我们主要的注意力就在DatanodeProtocol上,相应的,如果你们的项目需要在Client与Namenode之间添加一个通讯接口那么就主要集中在ClientProtocol的修改。

我们知道基于Protobuf实现的Rpc机制需要客户端和服务端实现共同的一个接口,以Datanode与Namenode之间的通讯为例,DatanodeProtocol.java中定义的接口就是Datanode与Namenode需要共同实现的接口,查看DatanodeProtocol.jave中接口DatanodeProtocol的定义如下:

@InterfaceAudience.Private
public interface DatanodeProtocol {
  /**
   * This class is used by both the Namenode (client) and BackupNode (server) 
   * to insulate from the protocol serialization.
   * 
   * If you are adding/changing DN's interface then you need to 
   * change both this class and ALSO related protocol buffer
   * wire protocol definition in DatanodeProtocol.proto.
   * 
   * For more details on protocol buffer wire protocol, please see 
   * .../org/apache/hadoop/hdfs/protocolPB/overview.html
   */
   ……
   }

注释中已经很好的说明了如果要对现有的Rpc作扩展要修改的地方:DatanodeProtocol.java,DatanodeProtocol.proto,其中后面这个是protobuf的文件,对protobuf语法还不是很了解的童鞋移步http://colobu.com/2015/01/07/Protobuf-language-guide/。里面定义了message,以及rpc call。而DatanodeProtocol.java中定义了Namenode和Datanode两端要实现的接口interface DatanodeProtocol。两个关键文件在项目中的继承关系如下图所示。我们对Datanode与Namenode之间的RPC的扩展时,需要修改的地方也就是下图中所展示的类。
这里写图片描述
这里以Datanode向Namenode的注册的RPC函数来作为例子来展示如何添加一个新的RPC 函数。从上图看出我们修改主要分为两条线一条是DatanodeProtocol.java线以及DatanodeProtocol.proto线,所以以Datanode注册的例子分析的时候也分为两条线分析:

DatanodeProtocol.proto线

首先DatanodeProtocol.proto中定义了用于注册的rpc函数如下所示,里面定义了Rpc函数,以及函数发送的消息以及返回的消息的构成。

message RegisterDatanodeRequestProto {
  required DatanodeRegistrationProto registration = 1; // Datanode info
}

message RegisterDatanodeResponseProto {
  required DatanodeRegistrationProto registration = 1; // Datanode info
}

service DatanodeProtocolService {
  /**
   * Register a datanode at a namenode
   */
  rpc registerDatanode(RegisterDatanodeRequestProto)
      returns(RegisterDatanodeResponseProto);
      ……
 }

在DatanodeProtocolPB中对使用protoc对DatanodeProtocol.proto编译产生的类进行了继承:

public interface DatanodeProtocolPB extends
    DatanodeProtocolService.BlockingInterface {
}

最后在DatanodeProtocolServerSideTranslatorPB对继承下来的进行了实现:

  @Override
  public RegisterDatanodeResponseProto registerDatanode(
      RpcController controller, RegisterDatanodeRequestProto request)
      throws ServiceException {
    DatanodeRegistration registration = PBHelper.convert(request
        .getRegistration());
    DatanodeRegistration registrationResp;
    try {
      registrationResp = impl.registerDatanode(registration);//!!!!!
    } catch (IOException e) {
      throw new ServiceException(e);
    }
    return RegisterDatanodeResponseProto.newBuilder()
        .setRegistration(PBHelper.convert(registrationResp)).build();
  }

从代码中我们可以看出,在DatanodeProtocolServerSideTranslatorPB中实现的registerDatanode函数中最终是调用了impl.registerDatanode(),追到这个impl赋值地方发现

  public DatanodeProtocolServerSideTranslatorPB(DatanodeProtocol impl,
      int maxDataLength) {
    this.impl = impl;
    this.maxDataLength = maxDataLength;
  }

看到了吗,在DatanodeProtocolServerSideTranslatorPB的构造函数中有一个参数是DatanodeProtocol 类型,也就是一个DatanodeProtocol的子类,这个子类里面实现了DatanodeProtocol接口中定义的registerDatanode函数,而实际Datanode向Namenode注册的功能也是在这个impl类实现的,下面我们会从Datanodeprotocol.java线进行讲。然后你就会发现在DatanodeProtocolServerSideTranslatorPB构造函数中传进来的DatanodeProtocol类的实现实际上是NamenodeRpcServer。NamenodeRpcServer中实际实现了注册的功能。

DatanodeProtocol.java线

interface DatanodeProtocol中定义了如下的接口:

  public DatanodeRegistration registerDatanode(DatanodeRegistration registration
      ) throws IOException;

NamenodeProtocols继承了所有Namenode这边所有需要实现的协议,其中包含了DatanodeProtocol:

@InterfaceAudience.Private
public interface NamenodeProtocols
  extends ClientProtocol,
          DatanodeProtocol,
          DatanodeLifelineProtocol,
          NamenodeProtocol,
          RefreshAuthorizationPolicyProtocol,
          RefreshUserMappingsProtocol,
          RefreshCallQueueProtocol,
          GenericRefreshProtocol,
          GetUserMappingsProtocol,
          HAServiceProtocol,
          TraceAdminProtocol {
}

NamenodeRpcServer继承实现了NamenodeProtocols,其中关于注册部分的实现如下所示:

  @Override // DatanodeProtocol
  public DatanodeRegistration registerDatanode(DatanodeRegistration nodeReg)
      throws IOException {
    checkNNStartup();
    verifySoftwareVersion(nodeReg);
    namesystem.registerDatanode(nodeReg);
    return nodeReg;
  }

值的一提的是NamenodeRpcServer中的registerDatanode中最后调用了namesystem.registerDatanode(),这里的namesystem是FSNamesystem 类型的变量,实际上维护注册的Datanode列表,维护Active Datanode列表等操作都是在这个namesystem中具体进行操作,当然这些不属于我们要更改的RPC的部分。继续在NamenodeRPCServer查看我们可以看到下面的代码:

    DatanodeProtocolServerSideTranslatorPB dnProtoPbTranslator = 
        new DatanodeProtocolServerSideTranslatorPB(this, maxDataLength);
    BlockingService dnProtoPbService = DatanodeProtocolService
        .newReflectiveBlockingService(dnProtoPbTranslator);

我们看到NamenodeRpcServer把this传入DatanodeProtocolServerSideTranslatorPB的构造函数中,这印证了我们上面说的,DatanodeProtocolServerSideTranslatorPB中调用的RegisterDatanode上通过调用NamenodeRpcServer中实现的注册的方法实现注册。这样两条线就串起来了。
最后看一下DatanodeProtocolClientSideTranslator中是如何实现的RegisterDatanode的

public DatanodeProtocolClientSideTranslatorPB(InetSocketAddress nameNodeAddr,
      Configuration conf) throws IOException {
    RPC.setProtocolEngine(conf, DatanodeProtocolPB.class,
        ProtobufRpcEngine.class);
    UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
    rpcProxy = createNamenode(nameNodeAddr, conf, ugi);//通过RPC.getProxy从Namenode获取
  }

  @Override
  public DatanodeRegistration registerDatanode(DatanodeRegistration registration
      ) throws IOException {
    RegisterDatanodeRequestProto.Builder builder = RegisterDatanodeRequestProto
        .newBuilder().setRegistration(PBHelper.convert(registration));
    RegisterDatanodeResponseProto resp;
    try {
      resp = rpcProxy.registerDatanode(NULL_CONTROLLER, builder.build());
    } catch (ServiceException se) {
      throw ProtobufHelper.getRemoteException(se);
    }
    return PBHelper.convert(resp.getRegistration());
  }

这里写图片描述
依据Datanode注册的例子,大家应该也看懂了如果自己添加一个RPC 调用该如何作,在DatnodeProtocol.java中添加接口,在DatanodeProtocol.proto中添加rpc函数以及需要的消息的定义,然后在上述的继承图中的类中是实现相关函数与接口,实现方式可以参照RegisterDatanode()

转载注明出处:http://blog.youkuaiyun.com/ningning1994

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值