加载完solr.xml后,再回到createCoreContainer方法:
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;
}
开始实例化CoreContainer,然后用这个实例来加载cores,load方法比较长,具体在注释中分析:
/**
* Load the cores defined for this CoreContainer
*/
public void load() {
log.info("Loading cores into CoreContainer [instanceDir={}]", loader.getInstanceDir());
// add the sharedLib to the shared resource loader before initializing cfg based plugins
// 这个cfg是solr.xml的属性封装的对象,取sharedLib属性。sharedLib: path to a lib directory that will be shared across all cores
String libDir = cfg.getSharedLibDirectory();
if (libDir != null) {
File f = FileUtils.resolvePath(new File(solrHome), libDir);
log.info("loading shared library: " + f.getAbsolutePath());
loader.addToClassLoader(libDir, null, false);
loader.reloadLuceneSPI();
}
// solr.xml中配置的shardHandlerFactory
shardHandlerFactory = ShardHandlerFactory.newInstance(cfg.getShardHandlerFactoryPluginInfo(), loader);
// updateShardHandler实例
updateShardHandler = new UpdateShardHandler( cfg);
// 取transientCacheSize属性,默认是int最大值,如果不是int最大值,则不起作用
solrCores.allocateLazyCores(cfg .getTransientCacheSize(), loader);
logging = LogWatcher.newRegisteredLogWatcher(cfg.getLogWatcherConfig(), loader);
// shareSchema属性,如果为true则共享IndexSchema
shareSchema = cfg.hasSchemaCache();
if (shareSchema) {
indexSchemaCache = new ConcurrentHashMap<String,IndexSchema>();
}
hostName = cfg.getHost();
log.info("Host Name: " + hostName);
// 初始化zookeeper
zkSys.initZooKeeper( this, solrHome, cfg);
collectionsHandler = createHandler(cfg.getCollectionsHandlerClass(), CollectionsHandler.class);
infoHandler = createHandler(cfg.getInfoHandlerClass(), InfoHandler.class);
coreAdminHandler = createHandler(cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class);
containerProperties = cfg.getSolrProperties( "solr");
// setup executor to load cores in parallel
// do not limit the size of the executor in zk mode since cores may try and wait for each other.
ExecutorService coreLoadExecutor = Executors.newFixedThreadPool(
( zkSys.getZkController() == null ? cfg.getCoreLoadThreadCount() : Integer. MAX_VALUE ),
new DefaultSolrThreadFactory( "coreLoadExecutor") );
try {
CompletionService<SolrCore> completionService = new ExecutorCompletionService<SolrCore>(
coreLoadExecutor);
Set<Future<SolrCore>> pending = new HashSet<Future<SolrCore>>();
// 遍历$solrhome寻找有core.properties的文件,
List<CoreDescriptor> cds = coresLocator.discover(this );
checkForDuplicateCoreNames(cds);
for (final CoreDescriptor cd : cds) {
final String name = cd.getName();
try {
if (cd.isTransient() || ! cd.isLoadOnStartup()) {
// Store it away for later use. includes non-transient but not
// loaded at startup cores.
solrCores.putDynamicDescriptor(name, cd);
}
if (cd.isLoadOnStartup()) { // The normal case
Callable<SolrCore> task = new Callable<SolrCore>() {
@Override
public SolrCore call() {
SolrCore c = null;
try {
if ( zkSys.getZkController() != null) {
preRegisterInZk(cd);
}
// 基于descriptor创建一个未注册的core
c = create(cd);
// 注册core
registerCore(cd.isTransient(), name, c, false, false );
} catch (Exception e) {
SolrException. log(log, null, e);
try {
/* if (isZooKeeperAware()) {
try {
zkSys.zkController.unregister(name, cd);
} catch (InterruptedException e2) {
Thread.currentThread().interrupt();
SolrException.log(log, null, e2);
} catch (KeeperException e3) {
SolrException.log(log, null, e3);
}
}*/
} finally {
if (c != null) {
c.close();
}
}
}
return c;
}
};
pending.add(completionService.submit(task));
}
} catch (Exception e) {
SolrException. log(log, null, e);
}
}
while (pending != null && pending.size() > 0) {
try {
Future<SolrCore> future = completionService.take();
if (future == null) return;
pending.remove(future);
try {
SolrCore c = future.get();
// track original names
if (c != null) {
solrCores.putCoreToOrigName(c, c.getName());
}
} catch (ExecutionException e) {
SolrException. log(SolrCore.log, "Error loading core", e);
}
} catch (InterruptedException e) {
throw new SolrException(SolrException.ErrorCode .SERVICE_UNAVAILABLE,
"interrupted while loading core", e);
}
}
// Start the background thread
backgroundCloser = new CloserThread( this, solrCores, cfg );
backgroundCloser.start();
} finally {
if (coreLoadExecutor != null) {
ExecutorUtil.shutdownNowAndAwaitTermination(coreLoadExecutor);
}
}
if (isZooKeeperAware()) {
// register in zk in background threads
Collection<SolrCore> cores = getCores();
if (cores != null) {
for (SolrCore core : cores) {
try {
zkSys.registerInZk(core, true);
} catch (Throwable t) {
SolrException. log(log, "Error registering SolrCore", t);
}
}
}
}
}
其中通过遍历solrhome文件夹下的所有文件夹,寻找core.properties文件,每找到一个配置文件视为找到一个core,并终止此文件夹的寻找,因此core.properties文件不能在嵌套的文件夹中出现,找到了core.properties文件夹,里面配置的属性封装为CoreDescriptor实例,同时将properties属性配置的文件作为扩展配置文件一并加载(默认solrcore.properties),solrcore.properties的作用可见https://cwiki.apache.org/confluence/display/solr/Configuring+solrconfig.xml#Configuringsolrconfig.xml-SubstitutingPropertiesinSolrConfigFiles
下面具体看几个相关的重要方法,以注释的形式分析:
先是coresLocator.discover(this)方法,coresLocator有两个子类:CorePropertiesLocator和SolrXMLCoresLocator,此处使用的是CorePropertiesLocator:
@Override
public List<CoreDescriptor> discover(CoreContainer cc) {
logger.info( "Looking for core definitions underneath {}", rootDirectory.getAbsolutePath());
List<CoreDescriptor> cds = Lists. newArrayList();
discoverUnder( rootDirectory, cds, cc );
logger.info( "Found {} core definitions", cds.size ());
return cds;
}
private void discoverUnder(File root, List<CoreDescriptor> cds, CoreContainer cc ) {
if (!root.exists())
return;
for (File child : root.listFiles()) {
File propertiesFile = new File(child, PROPERTIES_FILENAME);
if (propertiesFile.exists()) {
// 里面读property文件,返回时,new了一个CoreDescriptor
CoreDescriptor cd = buildCoreDescriptor(propertiesFile, cc );
logger.info( "Found core {} in {}", cd.getName(), cd.getInstanceDir());
cds.add(cd);
// 这里continue了,这就是为什么不支持嵌套的原因:https://cwiki.apache.org/confluence/display/solr/Format+of+solr.xml
continue;
}
if (child.isDirectory())
discoverUnder(child, cds, cc);
}
}
之后是CoreDescriptor的构造函数和里面加载solrcore.properties的方法:
/**
* Create a new CoreDescriptor.
* @param container the CoreDescriptor's container
* @param name the CoreDescriptor's name
* @param instanceDir a String containing the instanceDir
* @param coreProps a Properties object of the properties for this core
* @param params additional params
*/
public CoreDescriptor(CoreContainer container, String name, String instanceDir,
Properties coreProps, SolrParams params) {
// CoreDescriptor里还有个包含自己的final的CoreContainer的引用
this.coreContainer = container;
originalCoreProperties.setProperty( CORE_NAME, name);
originalCoreProperties.setProperty( CORE_INSTDIR, instanceDir);
Properties containerProperties = container.getContainerProperties();
name = PropertiesUtil.substituteProperty(checkPropertyIsNotEmpty(name , CORE_NAME),
containerProperties);
instanceDir = PropertiesUtil.substituteProperty(checkPropertyIsNotEmpty(instanceDir , CORE_INSTDIR),
containerProperties);
// 默认配置属性
coreProperties.putAll( defaultProperties);
coreProperties.put( CORE_NAME, name);
coreProperties.put( CORE_INSTDIR, instanceDir);
coreProperties.put( CORE_ABS_INSTDIR, convertToAbsolute(instanceDir, container.getCoreRootDirectory()));
for (String propname : coreProps.stringPropertyNames()) {
String propvalue = coreProps.getProperty(propname);
// 除了那些规定好的属性名外,其他的属性名视为用户自定义的属性(即"User defined properties from core.properties")
if (isUserDefinedProperty(propname))
originalExtraProperties.put(propname, propvalue);
else
originalCoreProperties.put(propname, propvalue);
if (!requiredProperties.contains(propname)) // Required props are already dealt with
coreProperties.setProperty(propname,
PropertiesUtil. substituteProperty(propvalue, containerProperties));
}
// 加载solrcore.properties
loadExtraProperties();
// 构建可替换的属性,其实就是把那几个标准属性(standardPropNames)都加上前缀“solr.core.”,然后放substitutableProperties里。
// substitutableProperties:The properties for this core, substitutable by resource loaders
buildSubstitutableProperties();
// TODO maybe make this a CloudCoreDescriptor subclass?
if (container.isZooKeeperAware()) {
// 如果是cloud模式,在new个CloudDescriptor实例
cloudDesc = new CloudDescriptor( name, coreProperties, this );
if (params != null) {
cloudDesc.setParams( params);
}
}
else {
cloudDesc = null;
}
}
/**
* Load properties specified in an external properties file.
*
* The file to load can be specified in a {@code properties} property on
* the original Properties object used to create this CoreDescriptor. If
* this has not been set, then we look for {@code conf/solrcore.properties}
* underneath the instance dir.
*
* File paths are taken as read from the core's instance directory
* if they are not absolute.
*/
protected void loadExtraProperties() {
// properties属性,不存在就取默认的conf\solrcore.properties
String filename = coreProperties .getProperty(CORE_PROPERTIES, DEFAULT_EXTERNAL_PROPERTIES_FILE );
// 绝对路径生成file
File propertiesFile = resolvePaths(filename);
if (propertiesFile.exists()) {
FileInputStream in = null;
try {
in = new FileInputStream(propertiesFile);
Properties externalProps = new Properties();
externalProps.load( new InputStreamReader(in, "UTF-8"));
// 文件存在,读进来,都放coreProperties,solrcore.properties和core.properties放一起了。
coreProperties.putAll(externalProps);
} catch (IOException e) {
String message = String. format(Locale.ROOT, "Could not load properties from %s: %s:" ,
propertiesFile.getAbsoluteFile(), e.toString());
throw new SolrException(SolrException.ErrorCode .SERVER_ERROR, message);
} finally {
IOUtils. closeQuietly(in);
}
}
}
找到了core之后,开始多线程创建core,创建core时还是从本地文件加载:createFromLocal,创建时,先加载solrconfig.xml,封装为SolrConfig对象,solrconfig.xml中定义了许多配置参数,这些都是作为PluginInfo保存在一个LinkedHashMap里;之后,再加载schema.xml,封装为IndexSchema对象;最后返回一个新的SolrCore实例,在新建SolrCore实例的时候做了很多工作,先看下createFromLocal方法:
// Helper method to separate out creating a core from local configuration files. See create()
private SolrCore createFromLocal(String instanceDir, CoreDescriptor dcore) {
SolrResourceLoader solrLoader = null;
SolrConfig config = null;
solrLoader = new SolrResourceLoader(instanceDir, loader.getClassLoader(), dcore.getSubstitutableProperties());
try {
// SolrConfig,封装了solrconfig.xml里面的属性
config = new SolrConfig(solrLoader, dcore.getConfigName(), null);
} catch (Exception e) {
log.error("Failed to load file {}", new File(instanceDir, dcore.getConfigName()).getAbsolutePath());
throw new SolrException(ErrorCode.SERVER_ERROR,
"Could not load config file " + new File(instanceDir, dcore.getConfigName()).getAbsolutePath(),
e);
}
// 开始加载schema.xml
IndexSchema schema = null;
if (indexSchemaCache != null) {
final String resourceNameToBeUsed = IndexSchemaFactory.getResourceNameToBeUsed(dcore.getSchemaName(), config);
File schemaFile = new File(resourceNameToBeUsed);
if (!schemaFile.isAbsolute()) {
schemaFile = new File(solrLoader.getConfigDir(), schemaFile.getPath());
}
if (schemaFile.exists()) {
String key = schemaFile.getAbsolutePath()
+ ":"
+ new SimpleDateFormat("yyyyMMddHHmmss", Locale.ROOT).format(new Date(
schemaFile.lastModified()));
schema = indexSchemaCache.get(key);
if (schema == null) {
log.info("creating new schema object for core: " + dcore.getName());
schema = IndexSchemaFactory.buildIndexSchema(dcore.getSchemaName(), config);
indexSchemaCache.put(key, schema);
} else {
log.info("re-using schema object for core: " + dcore.getName());
}
}
}
if (schema == null) {
schema = IndexSchemaFactory.buildIndexSchema(dcore.getSchemaName(), config);
}
SolrCore core = new SolrCore(dcore.getName(), null, config, schema, dcore);
if (core.getUpdateHandler().getUpdateLog() != null) {
// always kick off recovery if we are in standalone mode.
core.getUpdateHandler().getUpdateLog().recoverFromLog();
}
return core;
}
solrconfig.xml的加载是在实例化SolrConfig时来完的,里面的Handler,Component等属性片段都是以plugin的形式封装的:
/** Creates a configuration instance from a resource loader, a configuration name and a stream.
* If the stream is null, the resource loader will open the configuration stream.
* If the stream is not null, no attempt to load the resource will occur (the name is not used).
*@param loader the resource loader
*@param name the configuration name
*@param is the configuration stream
*/
public SolrConfig(SolrResourceLoader loader, String name, InputSource is)
throws ParserConfigurationException, IOException, SAXException {
// 父类Config的构造方法,这个Config主要属性有Document,prefix,name,SolrResourceLoader等。prefix是第四个参数传入的"/config/"
super(loader, name, is, "/config/“);
// 里面吧所有的lib标签里的jar加入到SolrResourceLoader
initLibs();
luceneMatchVersion = getLuceneVersion("luceneMatchVersion");
String indexConfigPrefix;
// Old indexDefaults and mainIndex sections are deprecated and fails fast for luceneMatchVersion=>LUCENE_40.
// For older solrconfig.xml's we allow the old sections, but never mixed with the new <indexConfig>
boolean hasDeprecatedIndexConfig = (getNode("indexDefaults", false) != null) || (getNode("mainIndex", false) != null);
boolean hasNewIndexConfig = getNode("indexConfig", false) != null;
if(hasDeprecatedIndexConfig){
if(luceneMatchVersion.onOrAfter(Version.LUCENE_40)) {
throw new SolrException(ErrorCode.FORBIDDEN, "<indexDefaults> and <mainIndex> configuration sections are discontinued. Use <indexConfig> instead.");
} else {
// Still allow the old sections for older LuceneMatchVersion's
if(hasNewIndexConfig) {
throw new SolrException(ErrorCode.FORBIDDEN, "Cannot specify both <indexDefaults>, <mainIndex> and <indexConfig> at the same time. Please use <indexConfig> only.");
}
log.warn("<indexDefaults> and <mainIndex> configuration sections are deprecated and will fail for luceneMatchVersion=LUCENE_40 and later. Please use <indexConfig> instead.");
defaultIndexConfig = new SolrIndexConfig(this, "indexDefaults", null);
mainIndexConfig = new SolrIndexConfig(this, "mainIndex", defaultIndexConfig);
indexConfigPrefix = "mainIndex";
}
} else {
defaultIndexConfig = mainIndexConfig = null;
indexConfigPrefix = "indexConfig";
}
nrtMode = getBool(indexConfigPrefix+"/nrtMode", true);
// Parse indexConfig section, using mainIndex as backup in case old config is used
// indexConfig里面的配置被封装为SolrIndexConfig
indexConfig = new SolrIndexConfig(this, "indexConfig", mainIndexConfig);
booleanQueryMaxClauseCount = getInt("query/maxBooleanClauses", BooleanQuery.getMaxClauseCount());
log.info("Using Lucene MatchVersion: " + luceneMatchVersion);
// Warn about deprecated / discontinued parameters
// boolToFilterOptimizer has had no effect since 3.1
if(get("query/boolTofilterOptimizer", null) != null)
log.warn("solrconfig.xml: <boolTofilterOptimizer> is currently not implemented and has no effect.");
if(get("query/HashDocSet", null) != null)
log.warn("solrconfig.xml: <HashDocSet> is deprecated and no longer recommended used.");
// TODO: Old code - in case somebody wants to re-enable. Also see SolrIndexSearcher#search()
// filtOptEnabled = getBool("query/boolTofilterOptimizer/@enabled", false);
// filtOptCacheSize = getInt("query/boolTofilterOptimizer/@cacheSize",32);
// filtOptThreshold = getFloat("query/boolTofilterOptimizer/@threshold",.05f);
useFilterForSortedQuery = getBool("query/useFilterForSortedQuery", false);
queryResultWindowSize = Math.max(1, getInt("query/queryResultWindowSize", 1));
queryResultMaxDocsCached = getInt("query/queryResultMaxDocsCached", Integer.MAX_VALUE);
enableLazyFieldLoading = getBool("query/enableLazyFieldLoading", false);
filterCacheConfig = CacheConfig.getConfig(this, "query/filterCache");
queryResultCacheConfig = CacheConfig.getConfig(this, "query/queryResultCache");
documentCacheConfig = CacheConfig.getConfig(this, "query/documentCache");
CacheConfig conf = CacheConfig.getConfig(this, "query/fieldValueCache”);
// fieldValueCache如果不指定,会生成一个默认的
if (conf == null) {
Map<String,String> args = new HashMap<String,String>();
args.put("name","fieldValueCache");
args.put("size","10000");
args.put("initialSize","10");
args.put("showItems","-1");
conf = new CacheConfig(FastLRUCache.class, args, null);
}
fieldValueCacheConfig = conf;
unlockOnStartup = getBool(indexConfigPrefix+"/unlockOnStartup", false);
useColdSearcher = getBool("query/useColdSearcher",false);
dataDir = get("dataDir", null);
if (dataDir != null && dataDir.length()==0) dataDir=null;
userCacheConfigs = CacheConfig.getMultipleConfigs(this, "query/cache");
// autowarming时将旧缓存移到新缓存时所用
org.apache.solr.search.SolrIndexSearcher.initRegenerators(this);
hashSetInverseLoadFactor = 1.0f / getFloat("//HashDocSet/@loadFactor",0.75f);
hashDocSetMaxSize= getInt("//HashDocSet/@maxSize",3000);
// http缓存
httpCachingConfig = new HttpCachingConfig(this);
Node jmx = getNode("jmx", false);
if (jmx != null) {
jmxConfig = new JmxConfiguration(true,
get("jmx/@agentId", null),
get("jmx/@serviceUrl", null),
get("jmx/@rootName", null));
} else {
jmxConfig = new JmxConfiguration(false, null, null, null);
}
maxWarmingSearchers = getInt("query/maxWarmingSearchers",Integer.MAX_VALUE);
// 各种片段都作为plugin封装,然后每个的plugin的class属性所对应实现或继承的Interface或abstract类的名称为key存储在一个map里。
// 这些属性一般都会从借口或父类继承一个init方法,在后面实例化时把xml中配置的属性init到实例里。
loadPluginInfo(SolrRequestHandler.class,"requestHandler",
REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK);
loadPluginInfo(QParserPlugin.class,"queryParser",
REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK);
loadPluginInfo(QueryResponseWriter.class,"queryResponseWriter",
REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK);
loadPluginInfo(ValueSourceParser.class,"valueSourceParser",
REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK);
loadPluginInfo(TransformerFactory.class,"transformer",
REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK);
loadPluginInfo(SearchComponent.class,"searchComponent",
REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK);
// TODO: WTF is up with queryConverter???
// it aparently *only* works as a singleton? - SOLR-4304
// and even then -- only if there is a single SpellCheckComponent
// because of queryConverter.setAnalyzer
loadPluginInfo(QueryConverter.class,"queryConverter",
REQUIRE_NAME, REQUIRE_CLASS);
// this is hackish, since it picks up all SolrEventListeners,
// regardless of when/how/why they are used (or even if they are
// declared outside of the appropriate context) but there's no nice
// way around that in the PluginInfo framework
loadPluginInfo(SolrEventListener.class, "//listener",
REQUIRE_CLASS, MULTI_OK);
loadPluginInfo(DirectoryFactory.class,"directoryFactory",
REQUIRE_CLASS);
loadPluginInfo(IndexDeletionPolicy.class,indexConfigPrefix+"/deletionPolicy",
REQUIRE_CLASS);
loadPluginInfo(CodecFactory.class,"codecFactory",
REQUIRE_CLASS);
loadPluginInfo(IndexReaderFactory.class,"indexReaderFactory",
REQUIRE_CLASS);
loadPluginInfo(UpdateRequestProcessorChain.class,"updateRequestProcessorChain",
MULTI_OK);
loadPluginInfo(UpdateLog.class,"updateHandler/updateLog");
loadPluginInfo(IndexSchemaFactory.class,"schemaFactory",
REQUIRE_CLASS);
updateHandlerInfo = loadUpdatehandlerInfo();
multipartUploadLimitKB = getInt(
"requestDispatcher/requestParsers/@multipartUploadLimitInKB", 2048 );
formUploadLimitKB = getInt(
"requestDispatcher/requestParsers/@formdataUploadLimitInKB", 2048 );
enableRemoteStreams = getBool(
"requestDispatcher/requestParsers/@enableRemoteStreaming", false );
// Let this filter take care of /select?xxx format
handleSelect = getBool(
"requestDispatcher/@handleSelect", true );
addHttpRequestToContext = getBool(
"requestDispatcher/requestParsers/@addHttpRequestToContext", false );
solrRequestParsers = new SolrRequestParsers(this);
Config.log.info("Loaded SolrConfig: " + name);
}
schema.xml的加载没有特殊的地方,如果缓存中没有,就通过IndexSchemaFactory.buildIndexSchema加载,到此每个core的solrconfig.xml和schema.xml就加载完了。
(待续)