深入剖析SolrCloud(二)


        上一篇介紹了SolrCloud的基本概念,從這一篇開始我將深入到其實現代碼中進行剖析。

SolrCloud最重要的一點就是引入了ZooKeeper來統一管理各種配置和狀態信息。zookeeper是一個開源分布式的服務,它提供了分布式協作,分布式同步,配置管理等功能. 其實現的功能與googlechubby基本一致.zookeeper的官方網站已經寫了一篇非常經典的概述性文章,請大家參閱:ZooKeeper: A Distributed Coordination Service for Distributed Applications.

        上一篇的示例中是在啟動每個solr服務器前,內嵌啟動了一個Zookeeper服務器,再將這幾台Zookeeper服務器組成一個集群,確保Solr集群信息的高可用性和容錯性。

      構建一個可用的Zookeeper集群,這就是SolrCloud要做的第一件工作。下面來看下SolrCloud是如何實現這一功能的:

1) 首先在web.xml中配置了一個filter

< filter >
     < filter-name >SolrRequestFilter </ filter-name >
     < filter-class >org.apache.solr.servlet.SolrDispatchFilter </ filter-class >
       </ filter >

   web容器啟動時會去加載並初始化SolrDispatchFilter這個filter,它的init方法會被調用,這個方法中做的最主要的事情是初始化一個Solr核容器。

  CoreContainer.Initializer init = createInitializer();
     //  web.xml configuration
     this.pathPrefix = config.getInitParameter( "path-prefix" );
this.cores = init.initialize();

 2) 初始化Solr核容器時,首先找到solr的根目錄,這個目錄下最重要的是solr.xml這個配置文件,這個配置文件用於初始化容器中加載的各個solr核,如果沒有提供solr.xml,則會啟用默認的配置信息:

   private  static  final String DEF_SOLR_XML ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
          "<solr persistent=\"false\">\n" +
          "  <cores adminPath=\"/admin/cores\" defaultCoreName=\"" + DEFAULT_DEFAULT_CORE_NAME + "\">\n" +
          "    <core name=\""+ DEFAULT_DEFAULT_CORE_NAME + "\" shard=\"${shard:}\" instanceDir=\".\" />\n" +
          "  </cores>\n" +
          "</solr>";

  3) 初始化過程的其中一步就是初始化Zookeeper服務器,你可以選擇單機的Zookeeper服務器,也可以構建Zookeeper集群,下面以集群為例進行代碼分析。

     if (zkRun !=  null) {
      zkServer =  new SolrZkServer(zkRun, zookeeperHost, solrHome, hostPort);
      zkServer.parseConfig();
      zkServer.start();
      
       //  set client from server config if not already set
       if (zookeeperHost ==  null) {
        zookeeperHost = zkServer.getClientString();
      }
}

         SolrZkServer類就是伴隨solr啟動的內嵌的Zookeeper服務器,首先來看parseConfig方法,它負責解析zoo.cfg文件,讀取Zookeeper啟動時所需要的配置信息,這些配置信息由SolrZkServerProps類表示,

首先設置Zookeeper存儲數據的目錄

     if (zkProps ==  null) {
      zkProps =  new SolrZkServerProps();
       //  set default data dir
      
//  TODO: use something based on IP+port???  support ensemble all from same solr home?
      zkProps.setDataDir(solrHome + '/' + "zoo_data");
      zkProps.zkRun = zkRun;
      zkProps.solrPort = solrPort;
}

然后讀取zoo.cfg配置文件中的信息,為啟動zookeeper服務器提供完整的配置信息,

      props = SolrZkServerProps.getProperties(solrHome + '/' + "zoo.cfg");
      SolrZkServerProps.injectServers(props, zkRun, zkHost);
      zkProps.parseProperties(props);

    下面是一個示例配置文件:

tickTime=2000
dataDir=/var/zookeeper/
clientPort=2181
initLimit=5
syncLimit=2
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888

注意,server.x這些行就指明了zookeeper集群所包含的機器名稱,每台Zookeeper服務器會使用3個端口來進行工作,其中第一個端口(端口1)用來做運行期間server間的通信,第二個端口(端口2)用來做leader election,另外還有一個端口(端口0)負責接收客戶端請求。那么一台機器怎樣確定自己是誰呢?這是通過dataDir目錄下的myid文本文件確定。myid文件只包含一個數字,內容就是所在ServerIDQuorumPeerConfig.myid

1) 准備好集群所需要的配置信息后,就可以啟動Zookeeper集群了。啟動時是生成一個Zookeeper服務器線程,根據配置信息來決定是單機還是集群模式,如果是單機模式,則生成ZooKeeperServerMain對象並啟動,如果是集群模式,則使用QuorumPeerMain對象啟動。最后將服務器線程設置為Daemon模式,就完成了Zookeeper服務器的啟動工作了。

     public  void start() {
        zkThread =  new Thread() {
            @Override
             public  void run() {
                 try {
                     if (zkProps.getServers().size() > 1) { // zk集群
                        QuorumPeerMain zkServer =  new QuorumPeerMain();
                        zkServer.runFromConfig(zkProps);
                         if (logger.isInfoEnabled()) {
                            logger.info("啟動zk服務器集群成功");
                        }
                    }  else { // 單機zk
                        ServerConfig sc =  new ServerConfig();
                        sc.readFrom(zkProps);
                        ZooKeeperServerMain zkServer =  new ZooKeeperServerMain();
                        zkServer.runFromConfig(sc);
                         if (logger.isInfoEnabled()) {
                            logger.info("啟動單機zk服務器成功");
                        }
                    }
                    logger.info("ZooKeeper Server exited.");
                }  catch (Throwable e) {
                    logger.error("ZooKeeper Server ERROR", e);
                     throw  new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);                    
                }
            }
        };
         if (zkProps.getServers().size() > 1) {
            logger.info("STARTING EMBEDDED ENSEMBLE ZOOKEEPER SERVER at port " + zkProps.getClientPortAddress().getPort());
        }  else {
            logger.info("STARTING EMBEDDED STANDALONE ZOOKEEPER SERVER at port " + zkProps.getClientPortAddress().getPort());            
        }
        
        zkThread.setDaemon( true);
        zkThread.start();
         try {
            Thread.sleep(500);  //  pause for ZooKeeper to start
        }  catch (Exception e) {
            logger.error("STARTING ZOOKEEPER", e);
        }
    }

為了驗證集群是否啟動成功,可以使用Zookeeper提供的命令行工具進行驗證,進入bin目錄下,運行:

zkCli.cmd –server zookeeper服務器地址1:端口

    這是連接到集群中1Zookeeper服務器,然后創建一個ZNode,往其中加入一些數據,你再連接到集群中其他的服務器上,查看數據是否一致,即可知道Zookeeper集群是否已經構建成功。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM