Hadoop源码分析之IPC服务端连接建立与方法调用

在前面两篇文章中分析了IPC中Server端的初始化与启动和 客户端IPC连接与方法调用下面来分析服务器当客户端进行方法调用时,将数据发送给服务器后,服务器的处理过程。服务器处理过程涉及的了多个线程与类,正式服务器端的各个部分的合作,才使得IPC机制简单且高效。

连接建立

在org.apache.hadoop.ipc.Client.Connection.setupConnection()方法中的NetUtils.connect(this.socket, server, 20000);这行代码执行之后,客户端就向服务器端发送了一个连接请求。客户端的连接请求统一在在服务器端的org.apache.hadoop.ipc.Server.Listener类中的run()方法中进行处理,即在Listener线程中接收客户端的连接请求。服务器端使用Java NIO技术来接收IPC客户端的连接请求和处理方法调用相关过程,关于Java NIO请参见Java NIO。Server.Listener类中的run()方法代码如下:

@Override
    public void run() {
      LOG.info(getName() + ": starting");
      SERVER.set(Server.this);
      while (running) {
        SelectionKey key = null;
        try {
          selector.select();
          Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
          while (iter.hasNext()) {
            key = iter.next();
            iter.remove();
            try {
              if (key.isValid()) {
                if (key.isAcceptable())
                  doAccept(key);
              }
            } catch (IOException e) {
            }
            key = null;
          }
        } catch (OutOfMemoryError e) {
          // we can run out of memory if we have too many threads
          // log the event and sleep for a minute and give 
          // some thread(s) a chance to finish
          LOG.warn("Out of Memory in server select", e);
          closeCurrentConnection(key, e);
          cleanupConnections(true);
          try { Thread.sleep(60000); } catch (Exception ie) {}
        } catch (Exception e) {
          closeCurrentConnection(key, e);
        }
        cleanupConnections(false);
      }
      LOG.info("Stopping " + this.getName());

      synchronized (this) {
        try {
          acceptChannel.close();
          selector.close();
        } catch (IOException e) { }

        selector= null;
        acceptChannel= null;
        
        // clean up all connections
        while (!connectionList.isEmpty()) {
          closeConnection(connectionList.remove(0));
        }
      }
    }

在run()方法中首先使用一个ThreadLocal遍历将当前的Listener线程与启动Listener线程的Server对象关联,然后进入到一个循环,循环结束的条件是启动Listener线程的服务器停止运行,即running变量为false值,否则Listener线程一直处于运行状态接收客户端的连接请求。循环内部是标准的Java NIO中SocketServer的代码,遍历连接请求,然后逐个处理,如果连接请求有效,就使用方法doAccept()方法处理。doAccept()方法接收一个SelectionKey对象作为参数,在Java NIO中SelectionKey保存了Socket读写相关的信息,如doAccept()方法中通过ServerSocketChannel server = (ServerSocketChannel) key.channel();获取到数据通道ServerSocketChannel对象server,然后通过server.accept()方法就接收了客户端的一个连接请求,与客户端建立了一个连接。连接建立成功之后就配置连接通道信息如设置为非阻塞,设置不使用Nagle算法等。然后就获取一个Reader线程,Reader对象是在Listener创建过程中创建好的,放在一个由数组构成的循环队列中,通过getReader方法获取到。对于Reader类的解释,很多博文上都说一个客户端对应一个Reader线程,但是我觉得不是这样的,因为一个Reader的个数是由ipc.server.read.threadpool.size这个属性指定的,其默认值是1,而客户端的在同一时间访问同一个IPC服务器的数量可能不是一定的,不可能事先就确定好IPC客户端的数量,而Reader中有一个Selector变量readSelector变量,Selector类是Java NIO中的一个选择器类,它可以检测多个通道(Channel),所以没有必要一个客户端对应一个Reader线程。在IPC机制中,Reader线程的数量是可以配置的,觉得这个变量可以配置的原因是通过配置Reader线程的数量而提高性能。不知道这样理解有没有错误,如果有错误欢迎同学们指出。谢谢。doAccept()方法的代码如下:

void doAccept(SelectionKey key) throws IOException,  OutOfMemoryError {
      Connection c = null;
      ServerSocketChannel server = (ServerSocketChannel) key.channel();
      SocketChannel channel;
      while ((channel = server.accept()) != null) {
        channel.configureBlocking(false);
        channel.socket().setTcpNoDelay(tcpNoDelay);
        Reader reader = getReader();
        try {
          reader.startAdd();
          SelectionKey readKey = reader.registerChannel(channel);
          c = new Connection(readKey, channel, System.currentTimeMillis());
          readKey.attach(c);
          synchronized (connectionList) {
            connectionList.add(numConnections, c);
            numConnections++;
          }
          if (LOG.isDebugEnabled())
            LOG.debug("Server connection from " + c.toString() +
                "; # active connections: " + numConnections +
                "; # queued calls: " + callQueue.size());          
        } finally {
          reader.finishAdd(); 
        }

      }
    }

doAccept()方法中通过getReader()方法获取到一个Reader对象之后,就调用Reader.startAdd()方法,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值