Solr4.7源码分析-启动篇(一)

Solr的web.xml配置文件中,配置了一个filter:

  <!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->
  < filter>
    <filter-name >SolrRequestFilter </filter-name >
    <filter-class> org.apache.solr.servlet.SolrDispatchFilter</filter-class >
  </ filter>

  < filter-mapping>
    <filter-name >SolrRequestFilter </filter-name >
    <url-pattern >/* </url-pattern >
  </ filter-mapping>
Solr启动时,通过这个SolrDispatchFilter的init方法来加载配置文件和实例化各个组件。

Solr使用本地文件启动的大概流程为:(暂不考虑SolrCloud的情况)
1. 获取solrhome:先后通过JNDI,system property,default directory三种方式尝试获取
2. 实例化启动过程中使用的类加载器SolrResourceLoader
3. 加载solrhome下的solr.xml文件,封装为ConfigSolr
4. 实例化一个CoreContainer,通过CoreContainer来加载cores
5. (CoreContainer加载cores)先遍历solrhome,寻找含有core.properties的文件夹,视为找到一个core
6. 多线程加载cores
7. 加载每个core时先加载solrconfig.xml封装为SolrConfig
8. 再加载schema.xml封装为IndexSchema
9. 最后实例化SolrCore

下面具体通过代码详细分析启动过程:

先来看下init方法:

  @Override
  public void init(FilterConfig config) throws ServletException
  {
    log.info("SolrDispatchFilter.init()");

    try {
      // web.xml configuration
      this.pathPrefix = config.getInitParameter( "path-prefix" );

      this.cores = createCoreContainer();
      log.info("user.dir=" + System.getProperty("user.dir"));
    }
    catch( Throwable t ) {
      // catch this so our filter still works
      log.error( "Could not start Solr. Check solr/home property and the logs");
      SolrCore.log( t );
      if (t instanceof Error) {
        throw (Error) t;
      }
    }

    log.info("SolrDispatchFilter.init() done");
  }

其中主要的一行代码是调用createCoreContainer方法,从这个方法已经可以看出大致的流程:

  /**
   * Override this to change CoreContainer initialization
   * @return a CoreContainer to hold this server's cores
   */
  protected CoreContainer createCoreContainer() {
    SolrResourceLoader loader = new SolrResourceLoader(SolrResourceLoader.locateSolrHome());
    // 加载solrhome/solr.xml作为ConfigSolr
    ConfigSolr config = loadConfigSolr(loader);
    CoreContainer cores = new CoreContainer(loader, config);
    cores.load();
    return cores;
  }
先看下第一句中的SolrResourceLoader.locateSolrHome(),这个方法看出获取solrhome的流程,先尝试JNDI,在尝试system property,最后使用默认地址:

  /**
   * Finds the solrhome based on looking up the value in one of three places:
   * <ol>
   *  <li>JNDI: via java:comp/env/solr/home </li>
   *  <li>The system property solr.solr.home </li>
   *  <li>Look in the current working directory for a solr/ directory</li>
   * </ol>
   *
   * The return value is normalized.  Normalization essentially means it ends in a trailing slash.
   * @return A normalized solrhome
   * @see #normalizeDir(String)
   */
  public static String locateSolrHome() {

    String home = null;
    // Try JNDI
    try {
      Context c = new InitialContext();
      home = (String)c.lookup("java:comp/env/" +project+"/home");
      log.info("Using JNDI solr.home: "+home );
    } catch (NoInitialContextException e) {
      log.info("JNDI not configured for "+ project+ " (NoInitialContextEx)");
    } catch (NamingException e) {
      log.info("No /"+project+"/home in JNDI");
    } catch( RuntimeException ex ) {
      log.warn("Odd RuntimeException while testing for JNDI: " + ex.getMessage());
    }
   
    // Now try system property
    if( home == null ) {
      String prop = project + ".solr.home";
      home = System.getProperty(prop);
      if( home != null ) {
        log.info("using system property "+prop+ ": " + home );
      }
    }
   
    // if all else fails, try
    if( home == null ) {
      home = project + '/';
      log.info(project + " home defaulted to '" + home + "' (could not find system property or JNDI)" );
    }
    return normalizeDir( home );
  }
之后实例化一个SolrResourceLoader,这个SolrResourceLoader的parents是ContextClassLoader,并且加载了./lib/下的文件:

  /**
   * <p>
   * This loader will delegate to the context classloader when possible,
   * otherwise it will attempt to resolve resources using any jar files
   * found in the "lib/" directory in the specified instance directory.
   * </p>
   *
   * @param instanceDir - base directory for this resource loader, if null locateSolrHome() will be used.
   * @see #locateSolrHome
   */
  public SolrResourceLoader( String instanceDir, ClassLoader parent, Properties coreProperties )
  {
    if( instanceDir == null ) {
      this.instanceDir = SolrResourceLoader.locateSolrHome();
      log.info("new SolrResourceLoader for deduced Solr Home: '{}'",
               this. instanceDir);
    } else{
      this.instanceDir = normalizeDir(instanceDir );
      log.info("new SolrResourceLoader for directory: '{}'",
               this. instanceDir);
    }
   
    this. classLoader = createClassLoader (null, parent);
    addToClassLoader("./lib/", null, true);
    reloadLuceneSPI();
    this.coreProperties = coreProperties;
  }

参数parent此时传的null,所以parent取的ContextClassLoader
  /**
   * Convenience method for getting a new ClassLoader using all files found
   * in the specified lib directory.
   */
  static URLClassLoader createClassLoader(final File libDir, ClassLoader parent) {
    if ( null == parent ) {
      parent = Thread.currentThread().getContextClassLoader();
    }
    return replaceClassLoader(URLClassLoader. newInstance( new URL[0], parent),
                              libDir, null);
  }

  /**
   * Adds every file/dir found in the baseDir which passes the specified Filter
   * to the ClassLoader used by this ResourceLoader.  This method <b>MUST</b>
   * only be called prior to using this ResourceLoader to get any resources, otherwise
   * it's behavior will be non -deterministic. You also have to {link @reloadLuceneSPI }
   * before using this ResourceLoader.
   *
   * <p>This method will quietly ignore missing or non-directory <code> baseDir</code>
   *  folder.
   *
   * @param baseDir base directory whose children (either jars or directories of
   *                classes) will be in the classpath, will be resolved relative
   *                the instance dir.
   * @param filter The filter files must satisfy, if null all files will be accepted.
   * @param quiet  Be quiet if baseDir does not point to a directory or if no file is
   *               left after applying the filter.
   */
  void addToClassLoader( final String baseDir, final FileFilter filter, boolean quiet ) {
    // 拼绝对路径
    File base = FileUtils.resolvePath(new File(getInstanceDir()), baseDir );
    if (base != null && base .exists() && base.isDirectory()) {
      File[] files = base .listFiles(filter );
      if (files == null || files. length == 0) {
        if (!quiet) {
          log.warn( "No files added to classloader from lib: "
                   + baseDir + " (resolved as: " + base.getAbsolutePath() + ").");
        }
      } else {
        this.classLoader = replaceClassLoader(classLoader, base , filter );
      }
    } else {
      if (!quiet) {
        log.warn("Can't find (or read) directory to add to classloader: "
            + baseDir + " (resolved as: " + base.getAbsolutePath() + ").");
      }
    }
  }
<span style="font-family:Tahoma;font-size:18px;color:#e30000;">
</span>
接着回到createCoreContainer里的loadConfigSolr,开始加载solr.xml:
  private ConfigSolr loadConfigSolr(SolrResourceLoader loader) {

    String solrxmlLocation = System.getProperty( "solr.solrxml.location" , "solrhome");

    if (solrxmlLocation == null || "solrhome".equalsIgnoreCase(solrxmlLocation))
      return ConfigSolr. fromSolrHome(loader, loader.getInstanceDir());

    if ("zookeeper".equalsIgnoreCase(solrxmlLocation)) {
      String zkHost = System. getProperty("zkHost");
      log.info("Trying to read solr.xml from " + zkHost);
      if (StringUtils.isEmpty(zkHost))
        throw new SolrException( ErrorCode. SERVER_ERROR,
            "Could not load solr.xml from zookeeper: zkHost system property not set");
      SolrZkClient zkClient = new SolrZkClient(zkHost, 30000);
      try {
        if (!zkClient. exists( "/solr.xml", true ))
          throw new SolrException( ErrorCode. SERVER_ERROR, "Could not load solr.xml from zookeeper: node not found");
        byte[] data = zkClient.getData( "/solr.xml", null , null, true );
        return ConfigSolr. fromInputStream(loader, new ByteArrayInputStream(data));
      } catch (Exception e) {
        throw new SolrException( ErrorCode. SERVER_ERROR, "Could not load solr.xml from zookeeper" , e);
      } finally {
        zkClient.close();
      }
    }

    throw new SolrException( ErrorCode. SERVER_ERROR,
        "Bad solr.solrxml.location set: " + solrxmlLocation + " - should be 'solrhome' or 'zookeeper'");
  }

在ConfigSolr.fromSolrHome()中加载:

  public static ConfigSolr fromSolrHome(SolrResourceLoader loader , String solrHome ) {
    // 默认“solr.xml”
    return fromFile( loader, new File(solrHome, SOLR_XML_FILE ));
  }


   public static ConfigSolr fromFile(SolrResourceLoader loader, File configFile) {
    log.info("Loading container configuration from {}", configFile.getAbsolutePath());

    InputStream inputStream = null;

    try {
      // solrhome/solr.xml不存在的话,看是不是zk中有,如果没有就用默认的DEF_SOLR_XML
      if (!configFile.exists()) {
         // 这个if里面的方法是根据zkHost和zkRun判断是否有zk,看注释5.0后要有变化
        if (ZkContainer.isZkMode()) {
          throw new SolrException(SolrException.ErrorCode .SERVER_ERROR,
              "solr.xml does not exist in " + configFile.getAbsolutePath() + " cannot start Solr" );
        }
        log.info("{} does not exist, using default configuration", configFile.getAbsolutePath());
        inputStream = new ByteArrayInputStream(ConfigSolrXmlOld.DEF_SOLR_XML .getBytes(Charsets.UTF_8));
      } else {
        // 这里的configFile是solrhome/solr.xml
        inputStream = new FileInputStream(configFile);
      }
      return fromInputStream(loader , inputStream);
    }
    catch (Exception e) {
      throw new SolrException(SolrException.ErrorCode .SERVER_ERROR,
          "Could not load SOLR configuration", e);
    }
    finally {
      IOUtils.closeQuietly(inputStream);
    }
  }


  public static ConfigSolr fromInputStream(SolrResourceLoader loader , InputStream is) {
    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ByteStreams.copy(is , baos);
      String originalXml = IOUtils.toString( new ByteArrayInputStream(baos.toByteArray()), "UTF-8" );
      ByteArrayInputStream dup = new ByteArrayInputStream(baos.toByteArray());
      Config config = new Config( loader, null, new InputSource(dup), null, false );
      return fromConfig (config, originalXml);
    }
    catch (Exception e) {
      throw new SolrException(SolrException.ErrorCode .SERVER_ERROR, e);
    }
  }


  public static ConfigSolr fromConfig(Config config , String originalXml) {
    // 这里兼容了solr.xml的新旧两种格式
    boolean oldStyle = ( config.getNode("solr/cores" , false ) != null);
    return oldStyle ? new ConfigSolrXmlOld( config, originalXml)
                    : new ConfigSolrXml( config);
  }


看看ConfigSolrXml的构造函数:
  public ConfigSolrXml(Config config) {
    super(config);
    try {
      // 校验旧格式的solr.xml,如果发现旧的格式抛SolrException
      checkForIllegalConfig();
      // 填充所有属性
      fillPropMap();
      config.substituteProperties();
      coresLocator = new CorePropertiesLocator(getCoreRootDirectory());
    }
    catch (IOException e) {
      throw new SolrException(SolrException.ErrorCode .SERVER_ERROR, e);
    }
  }

fillPropMap()中将所有solr.xml的属性填充到一个HashMap<CfgProp, String>中:propMap,到此就加载完了solr.xml,封装为了ConfigSolr。

(待续)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值