HDFS的数据通信机制

HDFS中很有意思的一点是,它的控制消息的传输和数据消息的传输采用的是不一样的模块。这也是接下来我想探讨的重点。

HDFS中所有的控制消息的传输都基于它自实现的RPC模块,之前已经讨论过。

但是,Hadoop自己的RPC机制不太适合大数据量的传输,因为两个Node之间只用一个socket进行通信,网络的吞吐量不一定上得去。

而事实上,HDFS确实没有用RPC机制传输数据消息。当HDFS中的DFSClient对DataNode上保存的文件数据进行读写的时候,它其实采用了另外一个机制.

简单而言,HDFS分为了三个部分:
NameNode,处于master的地位,维护了数据在DataNode上的分布情况,并且,还要负责一些调度任务;
DataNode,存储真实数据的地方;
DFSClient,一个client端,通过它提供的接口访问NameNode和DataNode;
三者之间的通信全部是基于TCP/Socket; 如图所示:

图中,连线表示两者之间存在通信,箭头一方表示接收请求,没有箭头的一端表示发起请求的一方;图中的黑色细线表示控制消息的通路,红色粗线表示数据消息的通路;
可以看得出来,NameNode是一个典型的Server端程序,它总是处于接受请求,返回响应的状态中。NameNode永远不会主动的向其它组件发起请求(依稀记得GFS的论文中也是这样做的)。如果NameNode需要向DataNode发送一些调度或者控制命令的话,必须等待DataNode向NameNode发送heartbeat之后,作为heartbeat的response返回给DataNode。
DataNode就比较忙了,它不仅需要定时的发送heartbeat给NameNode,并且heartbeat的返回往往还附带了很多的控制消息需要处理,同时,DataNode要接收DFSClient来的读写数据的请求和一些控制请求,最后,DataNode之间还有数据消息和控制消息的传输。

每个DataNode在启动的时候会创建一个线程DataXceiverServer来专门负责block数据的读写的链接。而DataXceiverServer做的事情很简单 --一旦有一个连接,就创建一个新的DataXceiver来处理这个连接:

public void run() {
    while (datanode.shouldRun) {
      try {
        Socket s = ss.accept();
        s.setTcpNoDelay(true);
        new Daemon(datanode.threadGroup,
            new DataXceiver(s, datanode, this)).start();
      } catch (SocketTimeoutException ignored) {
        // wake up to see if should continue to run
      } catch (IOException ie) {
        // ............
      } catch (Throwable te) {
        // ............
      }
    }
    try {
      ss.close();
    } catch (IOException ie) {
      // .......
    }
  }

 

DataXceiver也是一个线程,它负责处理对应的一个连接,主要完成4种任务:
opReadBlock: 读取一个block
opWriteBlock: 写一个block到disk上
opCopyBlock: 读一个block,然后送到指定的目的地
opReplaceBlock: 替换一个block

class DataXceiver extends DataTransferProtocol.Receiver
    implements Runnable, FSConstants {
  // ................
  /**
   * Read/write data from/to the DataXceiveServer.
   */
  public void run() {
    updateCurrentThreadName("Waiting for operation");
    DataInputStream in=null;
    try {
      in = new DataInputStream(
          new BufferedInputStream(NetUtils.getInputStream(s),
                                  SMALL_BUFFER_SIZE));
      final DataTransferProtocol.Op op = readOp(in);
      // Make sure the xciver count is not exceeded
      // ....
      processOp(op, in);
    } catch (Throwable t) {
      LOG.error(datanode.dnRegistration + ":DataXceiver",t);
    } finally {
      //.....
    }
  }
  /** Process op by the corresponding method. */
    protected final void processOp(Op op, DataInputStream in
        ) throws IOException {
      switch(op) {
      case READ_BLOCK:
        opReadBlock(in);
        break;
      case WRITE_BLOCK:
        opWriteBlock(in);
        break;
      case REPLACE_BLOCK:
        opReplaceBlock(in);
        break;
      case COPY_BLOCK:
        opCopyBlock(in);
        break;
      case BLOCK_CHECKSUM:
        opBlockChecksum(in);
        break;
      default:
        throw new IOException("Unknown op " + op + " in data stream");
      }
    }

posted @ 2012-12-19 22:02  shileiw  阅读(322)  评论(0编辑  收藏  举报