HBase1.0.0的RPC機制分析與源碼解讀(一)

HBase的RPC機制,除了使用protocal buf的工具之外都是利用java的原生API進行構造,RPC機制的解讀包括客戶端和服務器端兩個部分,本文主要就服務器端的服務運行機制以及工作流程進行簡要分析.
首先我們來分析一下一個典型的RPC服務器短的處理流程,如下圖所示:

從圖中可以清楚的看到當客戶端向服務器端發送一個請求的時候,最開始是被RPCServer中的Listener所監聽到的,如下面的代碼所示,HBase的RpcServer啓動的時候會啓動幾個處理線程:

    responder.start();
    listener.start();
    scheduler.start();
其中,Responder線程負責數據的request的回覆工作,listener負責監聽客戶端的請求,scheduler負責具體call的調度工作
      while (running) {
        SelectionKey key = null;
        try {
          selector.select(); // FindBugs IS2_INCONSISTENT_SYNC
          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 ignored) {
              if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
            }
            key = null;
          }
        }
listener的實現過程中運用了java nio的一些特性,主要是每個Listener線程又會管理這一個Reader的線程池,這些Reader具體負責從Socket Channel中讀取數據,並解析數據中的相關項,進而構造出可運行的CallRunner:
      Call call = new Call(id, this.service, md, header, param, cellScanner, this, responder,
              totalRequestSize,
              traceInfo);
      scheduler.dispatch(new CallRunner(RpcServer.this, call, userProvider));
RpcScheduler調用對應的RpcExcutor進行相應的處理,RpcExcutor中啓動了多個處理線程,這些線程從隊列中取出任務並且執行,
  protected void startHandlers(final String nameSuffix, final int numHandlers,
      final List<BlockingQueue<CallRunner>> callQueues,
      final int qindex, final int qsize, final int port) {
    final String threadPrefix = name + Strings.nullToEmpty(nameSuffix);
    for (int i = 0; i < numHandlers; i++) {
      final int index = qindex + (i % qsize);
      Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
          consumerLoop(callQueues.get(index));
        }
      });
      t.setDaemon(true);
      t.setName(threadPrefix + "RpcServer.handler=" + handlers.size() +
        ",queue=" + index + ",port=" + port);
      t.start();
      LOG.debug(threadPrefix + " Start Handler index=" + handlers.size() + " queue=" + index);
      handlers.add(t);
    }
  }
  protected void consumerLoop(final BlockingQueue<CallRunner> myQueue) {
    boolean interrupted = false;
    double handlerFailureThreshhold =
        conf == null ? 1.0 : conf.getDouble(HConstants.REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT,
          HConstants.DEFAULT_REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT);
    try {
      while (running) {
        try {
          CallRunner task = myQueue.take();
          try {
            activeHandlerCount.incrementAndGet();
            task.run();

其實繼續跟進代碼就會發現CallRunner的run方法最主要動作有兩個一個是獲取操作結果一個是返回操作結果給客戶:
       resultPair = this.rpcServer.call(call.service, call.md, call.param, call.cellScanner,
          call.timestamp, this.status);
  if (!call.isDelayed() || !call.isReturnValueDelayed()) {
        Message param = resultPair != null ? resultPair.getFirst() : null;
        CellScanner cells = resultPair != null ? resultPair.getSecond() : null;
        call.setResponse(param, cells, errorThrowable, error);
      }
      call.sendResponseIfReady();
到此結束就簡要介紹了一個完整的服務端RPC處理流程,該流程中涉及到的相關的類的關係如下圖所示:







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章