Alluxio原始碼分析:RPC框架淺析(二)

柏辰爸爸發表於2016-03-25

        Alluxio原始碼分析是一個基於記憶體的分散式檔案系統,和HDFS、HBase等一樣,也是由主從節點構成的。而節點之間的通訊,一般都是採用的RPC通訊模型。Alluxio中RPC是基於何種技術如何實現的呢?它對於RPC請求是如何處理的?都涉及到哪些元件?本文將針對這些問題,為您一一解答。

        繼《Alluxio原始碼分析:RPC框架淺析(一)》一文後,本文繼續講解Alluxio中RPC實現。

        3、Server端實現:RPC Server埠繫結、傳輸協議等引數設定、Server啟動

        AlluxioMaster是Alluxio中Master的實現,那麼RPC服務端server自然就會落在它身上了。我們先看AlluxioMaster程式的啟動main()方法,如下:

  /**
   * Starts the Alluxio master server via {@code java -cp <ALLUXIO-VERSION> alluxio.Master}.
   *
   * @param args there are no arguments used
   */
  public static void main(String[] args) {
    
	// 啟動master時引數應為空
	if (args.length != 0) {
      LOG.info("java -cp {} alluxio.Master", Version.ALLUXIO_JAR);
      System.exit(-1);
    }

    // validate the conf
	// 驗證配置資訊
    if (!ValidateConf.validate()) {
      LOG.error("Invalid configuration found");
      System.exit(-1);
    }

    try {
    	
      // 呼叫get()方法,返回AlluxioMaster例項master
      AlluxioMaster master = get();
      
      // 呼叫例項master的start()方法,啟動AlluxioMaster例項master
      master.start();
    } catch (Exception e) {
      LOG.error("Uncaught exception terminating Master", e);
      System.exit(-1);
    }
  }

        它主要乾了兩件事,一個就是呼叫get()方法,返回AlluxioMaster例項master,另一個就是呼叫例項master的start()方法,啟動AlluxioMaster例項master。我們先看下get()方法,如下:

  /**
   * Returns a handle to the Alluxio master instance.
   *
   * @return Alluxio master handle
   */
  public static synchronized AlluxioMaster get() {
    
	// 靜態AlluxioMaster型別成員變數sAlluxioMaster為空時,通過Factory.create()構造一個,否則返回sAlluxioMaster
	if (sAlluxioMaster == null) {
      sAlluxioMaster = Factory.create();
    }
    return sAlluxioMaster;
  }

        而Factory的create()方法,則會根據引數alluxio.zookeeper.enabled確定返回FaultTolerantAlluxioMaster例項還是AlluxioMaster例項,FaultTolerantAlluxioMaster繼承自AlluxioMaster,預設是返回AlluxioMaster例項,程式碼如下:

    /**
     * @return {@link FaultTolerantAlluxioMaster} if Alluxio configuration is set to use zookeeper,
     *         otherwise, return {@link AlluxioMaster}.
     */
    public static AlluxioMaster create() {
    	
      // 根據引數alluxio.zookeeper.enabled確定返回FaultTolerantAlluxioMaster例項還是AlluxioMaster例項,
      // FaultTolerantAlluxioMaster繼承自AlluxioMaster,預設是返回AlluxioMaster例項
      if (MasterContext.getConf().getBoolean(Constants.ZOOKEEPER_ENABLED)) {
        return new FaultTolerantAlluxioMaster();
      }
      return new AlluxioMaster();
    }

        在AlluxioMasterd的構造方法中,涉及RPC相關的,主要是Worker最大和最小執行緒數、服務端Socker的TServerSocket例項mTServerSocket等的構造,關鍵程式碼如下:

    Configuration conf = MasterContext.getConf();

    // Worker最大和最小執行緒數:分別取引數alluxio.master.worker.threads.max和alluxio.master.worker.threads.min
    mMinWorkerThreads = conf.getInt(Constants.MASTER_WORKER_THREADS_MIN);
    mMaxWorkerThreads = conf.getInt(Constants.MASTER_WORKER_THREADS_MAX);

      // 獲取傳輸提供者mTransportProvider
      mTransportProvider = TransportProvider.Factory.create(conf);
      
      // 構造TServerSocket例項mTServerSocket
      // port取引數alluxio.master.port=19998
      mTServerSocket =
          new TServerSocket(NetworkAddressUtils.getBindAddress(ServiceType.MASTER_RPC, conf));

        再看例項master的start()方法,也就是AlluxioMaster的start()方法,程式碼如下:

  /**
   * Starts the Alluxio master server.
   *
   * @throws Exception if starting the master fails
   */
  public void start() throws Exception {
    startMasters(true);
    
    // 啟動服務
    startServing();
  }

        繼續看startServing()方法,如下:

  protected void startServing(String startMessage, String stopMessage) {
    mMasterMetricsSystem.start();
    
    // 啟動web服務
    startServingWebServer();
    LOG.info("Alluxio Master version {} started @ {} {}", Version.VERSION, mMasterAddress,
        startMessage);
    
    // 啟動RPC服務
    startServingRPCServer();
    LOG.info("Alluxio Master version {} ended @ {} {}", Version.VERSION, mMasterAddress,
        stopMessage);
  }

        撇開啟動web服務不說,我們看下啟動RPC服務的startServingRPCServer()方法,如下:

  protected void startServingRPCServer() {
    // set up multiplexed thrift processors
	// 構造多路複用處理器TMultiplexedProcessor例項processor
    TMultiplexedProcessor processor = new TMultiplexedProcessor();
    
    // 註冊BlockMaster服務
    registerServices(processor, mBlockMaster.getServices());
    
    // 註冊FileSystemMaster服務
    registerServices(processor, mFileSystemMaster.getServices());
    
    // 必要的話,註冊LineageMaster服務
    if (LineageUtils.isLineageEnabled(MasterContext.getConf())) {
      registerServices(processor, mLineageMaster.getServices());
    }
    
    // register additional masters for RPC service
    // 註冊額外的Masters服務
    for (Master master : mAdditionalMasters) {
      registerServices(processor, master.getServices());
    }

    // Return a TTransportFactory based on the authentication type
    TTransportFactory transportFactory;
    try {
    	
      // 獲得傳輸工廠例項
      transportFactory = mTransportProvider.getServerTransportFactory();
    } catch (IOException e) {
      throw Throwables.propagate(e);
    }

    // create master thrift service with the multiplexed processor.
    
    // 構造TThreadPoolServer例項時需要的引數:
    // 服務端Socker:TServerSocket型別例項mTServerSocket
    // 最大Worker執行緒數mMaxWorkerThreads
    // 最小Worker執行緒數mMinWorkerThreads
    // 處理器processor
    // 傳輸工廠transportFactory
    // 協議工廠TBinaryProtocol:二進位制協議TBinaryProtocol
    Args args = new TThreadPoolServer.Args(mTServerSocket).maxWorkerThreads(mMaxWorkerThreads)
        .minWorkerThreads(mMinWorkerThreads).processor(processor).transportFactory(transportFactory)
        .protocolFactory(new TBinaryProtocol.Factory(true, true));
    if (MasterContext.getConf().getBoolean(Constants.IN_TEST_MODE)) {
      args.stopTimeoutVal = 0;
    } else {
      args.stopTimeoutVal = Constants.THRIFT_STOP_TIMEOUT_SECONDS;
    }
    
    // 構造TThreadPoolServer例項mMasterServiceServer
    mMasterServiceServer = new TThreadPoolServer(args);

    // start thrift rpc server
    // 標誌位正在提供服務mIsServing設定為true
    mIsServing = true;
    // 啟動時間mStartTimeMs取當前時間
    mStartTimeMs = System.currentTimeMillis();
    
    // 啟動TThreadPoolServer服務
    mMasterServiceServer.serve();
  }

         startServingRPCServer()方法主要處理流程如下:

        1、構造多路複用處理器TMultiplexedProcessor例項processor;

        2、呼叫registerServices()方法,註冊BlockMaster、FileSystemMaster、LineageMaster、額外的Masters等各種服務;

        3、呼叫TransportProvider的getServerTransportFactory()方法獲得傳輸工廠例項transportFactory;

        4、構造TThreadPoolServer例項時需要的引數:包括:

             (1)服務端Socker:TServerSocket型別例項mTServerSocket;

             (2)最大Worker執行緒數mMaxWorkerThreads;

             (3)最小Worker執行緒數mMinWorkerThreads;

             (4)多路複用處理器processor;

             (5)傳輸工廠transportFactory;

             (6)協議工廠TBinaryProtocol:二進位制協議TBinaryProtocol;

        5、構造TThreadPoolServer例項mMasterServiceServer;

        6、標誌位正在提供服務mIsServing設定為true;

        7、啟動時間mStartTimeMs取當前時間;

        8、呼叫mMasterServiceServer的serve()方法啟動TThreadPoolServer服務。
        先以FileSystemMaster服務為例,看下RPC服務是如何註冊的,程式碼如下:

  private void registerServices(TMultiplexedProcessor processor, Map<String, TProcessor> services) {
    for (Map.Entry<String, TProcessor> service : services.entrySet()) {
      processor.registerProcessor(service.getKey(), service.getValue());
    }
  }

        註冊很簡單,多路複用處理器processor的registerProcessor()方法即可完成註冊,關鍵是要看註冊的是什麼東西它的服務是通過FileSystemMaster的getServices()方法獲取的,我們跟蹤下:

  @Override
  public Map<String, TProcessor> getServices() {
    Map<String, TProcessor> services = new HashMap<String, TProcessor>();
    
    // FileSystemMasterClientService服務
    services.put(
    		
        // key為"FileSystemMasterClient"
        Constants.FILE_SYSTEM_MASTER_CLIENT_SERVICE_NAME,
        
        // 可以看出,FileSystemMasterClientService服務Master端實現者是FileSystemMasterClientServiceHandler
        new FileSystemMasterClientService.Processor<FileSystemMasterClientServiceHandler>(
            new FileSystemMasterClientServiceHandler(this)));
    
    // FileSystemMasterWorkerService服務  
    services.put(
    		
        // key為"FileSystemMasterWorker"
        Constants.FILE_SYSTEM_MASTER_WORKER_SERVICE_NAME,
        
        // 可以看出,FileSystemMasterWorkerService服務Master端實現者是FileSystemMasterWorkerServiceHandler
        new FileSystemMasterWorkerService.Processor<FileSystemMasterWorkerServiceHandler>(
            new FileSystemMasterWorkerServiceHandler(this)));
    return services;
  }

        就倆服務:FileSystemMasterClientService和FileSystemMasterWorkerService,它們在Master端的實現者分別是FileSystemMasterClientServiceHandler和FileSystemMasterWorkerServiceHandler,並且是通過各自Service的Processor來構造的,看到這裡,你似乎應該明白什麼了吧!這就是Processor的用途。
        再看下獲得傳輸工廠例項transportFactory,它是通過TransportProvider例項mTransportProvider的getServerTransportFactory()方法來獲取的,而mTransportProvider的初始化也是在AlluxioMaster構造方法中,通過TransportProvider.Factory.create(conf)來獲取的,我們看下程式碼:

    public static TransportProvider create(Configuration conf) {
      AuthType authType = conf.getEnum(Constants.SECURITY_AUTHENTICATION_TYPE, AuthType.class);
      switch (authType) {
        case NOSASL:
          return new NoSaslTransportProvider(conf);
        case SIMPLE: // intended to fall through
        case CUSTOM:
          return new PlainSaslTransportProvider(conf);
        case KERBEROS:
          throw new UnsupportedOperationException(
              "getClientTransport: Kerberos is not supported currently.");
        default:
          throw new UnsupportedOperationException(
              "getClientTransport: Unsupported authentication type: " + authType.getAuthName());
      }
    }

        它目前僅支援NOSASL和CUSTOM兩種型別,分別對應NoSaslTransportProvider和PlainSaslTransportProvider兩個類。我們以CUSTOM型別的PlainSaslTransportProvider為例來看下getServerTransportFactory()方法,程式碼如下:

  @Override
  public TTransportFactory getServerTransportFactory() throws SaslException {
    AuthType authType =
        mConfiguration.getEnum(Constants.SECURITY_AUTHENTICATION_TYPE, AuthType.class);
    TSaslServerTransport.Factory saslFactory = new TSaslServerTransport.Factory();
    AuthenticationProvider provider =
        AuthenticationProvider.Factory.create(authType, mConfiguration);
    saslFactory
        .addServerDefinition(PlainSaslServerProvider.MECHANISM, null, null,
            new HashMap<String, String>(), new PlainSaslServerCallbackHandler(provider));
    return saslFactory;
  }

        剩餘的TThreadPoolServer例項構造、引數選擇等上面解釋的已經很清晰,讀者可自行分析。

        未完待續,請關注《Alluxio原始碼分析:RPC框架淺析(三)》


相關文章