Hadoop DataNode启动之创建NameNode代理(二)

本文详细解析了Hadoop中RPC机制的工作流程,包括客户端如何通过socket连接到NameNode,进行RPC调用并验证版本信息,以及客户端和服务端之间的数据交互过程。

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

上一篇讲解了DN如何和NN进行socket连接,在获得一个NN的代理时,连接完毕后会做一个RPC调用,获得服务端的getProtocolVersion,然后和客户端比较,如果相同则会返回代理对象,可以说又是一次暗号对接,继续看getProxy函数
public static VersionedProtocol getProxy(
      Class<? extends VersionedProtocol> protocol,
      long clientVersion, InetSocketAddress addr, UserGroupInformation ticket,
      Configuration conf, SocketFactory factory, int rpcTimeout) throws IOException {
    if (UserGroupInformation.isSecurityEnabled()) {
      SaslRpcServer.init(conf);
		}
		//在这里获得NN代理,建立与NN的socket连接
    VersionedProtocol proxy =
        (VersionedProtocol) Proxy.newProxyInstance(
            protocol.getClassLoader(), new Class[] { protocol },
            new Invoker(protocol, addr, ticket, conf, factory, rpcTimeout));
     //获得服务端协议版本,该调用会通知NN调用相同的函数,并把结果返回给客户端
 	long serverVersion = proxy.getProtocolVersion(protocol.getName(), 
                                                  clientVersion);
    //版本比较,在上一篇已经提过
    if (serverVersion == clientVersion) {
      return proxy;
    } else {
      throw new VersionMismatch(protocol.getName(), clientVersion, 
                                serverVersion);
    }
}
现在看getProtocolVersion是如何执行的
  public Writable call(Writable param, ConnectionId remoteId)  
                       throws InterruptedException, IOException {
		Call call = new Call(param);
		//获得连接
    Connection connection = getConnection(remoteId, call);
    //发送call对象,下面会继续介绍
		connection.sendParam(call);                 // send the parameter
    boolean interrupted = false;
    synchronized (call) {
      while (!call.done) {
        try {
          //如果服务端没有返回则一直等待在这里
          call.wait();                           // wait for the result
        } catch (InterruptedException ie) {
          // save the fact that we were interrupted
          interrupted = true;
        }
      }
........
}
下面看具体的发送格式
public void sendParam(Call call) {
      if (shouldCloseConnection.get()) {
        return;
      }
      DataOutputBuffer d=null;
      try {
        synchronized (this.out) {
          if (LOG.isDebugEnabled())
            LOG.debug(getName() + " sending #" + call.id);
         
          //建立输出缓冲区
          d = new DataOutputBuffer();
         //写入调用ID
          d.writeInt(call.id);
         //写入调用参数格式为:方法名、参数个数(4字节Int)、具体参数
          call.param.write(d);
          byte[] data = d.getData();
          int dataLength = d.getLength();
          out.writeInt(dataLength);      //first put the data length
          out.write(data, 0, dataLength);//write the data
          //刷新缓冲区
          out.flush();
        }
      } catch(IOException e) {
        markClosed(e);
      } finally {
        //关闭这个临时缓冲区
        IOUtils.closeStream(d);
      }
}
到目前为止,发送至服务端的调用已经发送完毕,就等服务端的返回了

上一篇中已经介绍过,为了接受返回值,DD已经启动了接收线程用于接收服务端的返回值


private void receiveResponse() {
      if (shouldCloseConnection.get()) {
        return;
      }
      //更新连接时间戳
      touch();
      try {
        //服务端返回前一直阻塞在这里,知道返回,首先读取ID号,如果调用成功则删除
        int id = in.readInt();                    // try to read an id
        if (LOG.isDebugEnabled())
          LOG.debug(getName() + " got value #" + id);
        Call call = calls.get(id);
        //获得调用标志,是否成功
        int state = in.readInt();     // read call status
        if (state == Status.SUCCESS.state) {
          //构建保留返回值的对象
          Writable value = ReflectionUtils.newInstance(valueClass, conf);
          //获得返回值
          value.readFields(in);                 // read value
          //更新call的成员变量
          call.setValue(value);
          //删除call对象,在这里删除后,继续回到run函数,进而main线程的wait函数会被唤醒
          //接受线程会被销毁
          calls.remove(id);
        } else if (state == Status.ERROR.state) {
          call.setException(new RemoteException(WritableUtils.readString(in),
                                                WritableUtils.readString(in)));
          calls.remove(id);
        } else if (state == Status.FATAL.state) {
          // Close the connection
          markClosed(new RemoteException(WritableUtils.readString(in), 
                                         WritableUtils.readString(in)));
        }
      } catch (IOException e) {
        markClosed(e);
      }
}
至此,client的call对象已经获得返回值,主线程继续执行getProtocolVersion已经有了返回值,如下
public static VersionedProtocol getProxy(
      Class<? extends VersionedProtocol> protocol,
      long clientVersion, InetSocketAddress addr, UserGroupInformation ticket,
      Configuration conf, SocketFactory factory, int rpcTimeout) throws IOException {


    if (UserGroupInformation.isSecurityEnabled()) {
      SaslRpcServer.init(conf);
    }
    VersionedProtocol proxy =
        (VersionedProtocol) Proxy.newProxyInstance(
            protocol.getClassLoader(), new Class[] { protocol },
            new Invoker(protocol, addr, ticket, conf, factory, rpcTimeout));
    //此处serverVersion已经有了返回值,一旦和客户端版本校验成功,则会返回代理对象
long serverVersion = proxy.getProtocolVersion(protocol.getName(), 
                                                  clientVersion);
if (serverVersion == clientVersion) {
  //校验成功后,这里直接返回代理对象
      return proxy;
    } else {
      throw new VersionMismatch(protocol.getName(), clientVersion, 
                                serverVersion);
    }
}
整个代理创建的过程就分析到这里,主要流程为创建代理实例并验证,在创建代理实例中并未和服务端通信,一直到RPC调用,核对版本信息后才能返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值