这一个是比较神奇的Handler,生于solr-5.0
,至今默默无闻。她的神奇是因为除了代码和SOLR-6924之外,好像没有一个地方提及她了,包括UserGuide和Wiki。但是呢,她又是比较实用的一个Handler,她不应该安静和沉寂,她需要被发现和使用。
她提供一个非常实用功能,至少对我来说是这样的。她提供一个实时且动态的获取和更新solrconfig.xml
配置的功能。其实这么说并不准确,但可以先这么理解。因为SolrConfigHandler
并没有直接更新solrconfig.xml
,而且是在zookeeper中的solrconfig.xml
同目录下生成一个configoverlay.json
文件用于存储更新配置项。格式当然是 json 了啦。
以前我们想更新solrconfig.xml
是一个比较麻烦的过程。先是更新solrconfig.xml
文件,重加载对应的Collection。这个过程若是在程序中控制就更加麻烦了。
现在不用了,可以直接通过SolrConfigHandler
完成,使代码变得简洁的清晰。
场景
我们知道增量索引,当然希望Solr频繁的发生soft commit
,为了使索引可见嘛。我们也知道soft commit
也是会消耗一定的性能的嘛,同时带来频繁的Merge
嘛。
然而我们重刷索引的时候,并不希望Solr频繁的发生soft commit
,甚至是hard commit
。为了不影响旧索引完整性也好,为了性能也好。总之我们期望Solr的commit
频率可控嘛,此时就需要更新solrconfig.xml
或者其它方式来控制commit
的配置。
SolrConfigHandler是一个Handler,所以她也是Solr的一个Plugin,那么她就可以在solrconfig.xml上修改其配置。刚说过了,她除代码和一个ISSUE之外没有其它地方提及,连solrconfig.xml也是。她是默认存在的,而且打开可以编辑的功能。需要关闭可编辑的功能,只需要在solrconfig.xml加上如配置即可。或者在JVM加一个 -Ddisable.configEdit=true
。
<requestHandler name="config" class="solr.SolrConfigHandler">
<lst name="defaults">
<str name="immutable">true</str>
</lst>
</requestHandler>
下面介绍这个神奇的Handler,它在没任意介绍的情况下,采用一种比较新奇使用方式。我们已经知道SolrConfigHandler
主要提供两个功能,查询配置信息
和更改配置信息
。对应SolrConfigHandler也是非常清晰,获取配置信息用METHOD.GET
,而更改配置信息用的是METHOD.POST
。这个逻辑非常清晰。
看一下代码,我们已经知道Handler需要实现抽象类RequestHandlerBase
,同时还需要实现handleRequestBody()
方法,并把逻辑放在这里。
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
setWt(req, CommonParams.JSON); // 返回结果以json的形式表示
String httpMethod = (String) req.getContext().get("httpMethod");
Command command = new Command(req, rsp, httpMethod);
if ("POST".equals(httpMethod)) {
if (configEditing_disabled || isImmutableConfigSet) {
final String reason = configEditing_disabled ? "due to " + CONFIGSET_EDITING_DISABLED_ARG : "because ConfigSet is immutable";
throw new SolrException(SolrException.ErrorCode.FORBIDDEN, " solrconfig editing is not enabled " + reason);
}
try {
command.handlePOST(); // 更改配置信息
} finally {
RequestHandlerUtils.addExperimentalFormatWarning(rsp);
}
} else {
command.handleGET(); // 获取配置信息
}
}
先看一下获取配置信息的逻辑吧。
// class Command
private Command(SolrQueryRequest req, SolrQueryResponse resp, String httpMethod) {
this.req = req;
this.resp = resp;
this.method = httpMethod;
path = (String) req.getContext().get("path");
if (path == null)
path = getDefaultPath();
parts = StrUtils.splitSmart(path, '/');
if (parts.get(0).isEmpty())
parts.remove(0);
}
private void handleGET() { // 代码有删减
if (parts.size() == 1) {
// this is the whole config. sent out the whole payload
resp.add("config", getConfigDetails()); // 如其名
} else {
if (ConfigOverlay.NAME.equals(parts.get(1))) { // overlay
resp.add(ConfigOverlay.NAME, req.getCore().getSolrConfig().getOverlay().toMap());
} else if (RequestParams.NAME.equals(parts.get(1))) { // params
} else {
if (ZNODEVER.equals(parts.get(1))) { // znodeVersion
} else {
Map<String, Object> m = getConfigDetails();
resp.add("config", makeMap(parts.get(1), m.get(parts.get(1))));
}
}
}
}
SolrConfigHandler中的parts,在Command的构造方法里被初始化,parts是URL中path部分的以为‘/’
进行切分成一个List。即以 /config开始,config为第一个元素。
1. 如果只有一个元素
2. 如果有二个元素
1. 第二个元素是 overlay : 指是读取configOverlay.json文件的内容并返回
2. 第二个元素是 params :是指读Request中RequestParams的内容并返回 (两个以上)
3. 第二个元素是 znodeVersion :读取zk相关节点的版本号
4. 第二个元素是其它的东西 :这里其它是指 solrconfig.xml 出现plugin名,则返回plugin名对应的配置信息(configOverlay.json会覆盖solrconfig.xml 的配置)
我们已经说过了,更改的配置信息不会直接更新
solrconfig.xml
,而是存储在configoverlay.json
里。但是呢,configoverlay.json
是可以修改的。Solr读取时先solrconfig.xml
的信息,然后再读configoverlay.json
,并以它的最新值覆盖solrconfig.xml
的信息。
SolrConfigHandler
读取配置信息时,其最小粒度是plugin
。
例如
solr/collection_name/config/updateHandler
{
"responseHeader":{
"status":0,
"QTime":0},
"config":{"updateHandler":{
"indexWriter":{"closeWaitsForMerges":true},
"commitWithin":{"softCommit":true},
"autoCommit":{
"maxDocs":-1,
"maxTime":900000,
"openSearcher":false},
"autoSoftCommit":{
"maxDocs":-1,
"maxTime":90000}}}}
如果第二个元素不是 overlay,params,znodeVersion也不是Plugin名时,报404
示例:在Java程序中直接获取Plugin的配置,有两个方式,最佳实践是自己写一对SolrRequest和SolrResponse。方式二如下:
SolrClient client =
Map<String, String> map = Maps.newHashMap();
map.put(CommonParams.QT, "/config/updateHandler");
SolrParams params = new MapSolrParams(map);
QueryResponse query = client.query(params, METHOD.GET);
接下来看看更改配置信息
部分。
其实我个人的观点,除非必要或者有兴趣可以看看源码,否则能不看就不看了吧。这里不再堆handlePost()
的源码,有兴趣可以自己看看,还是比较清晰的。
上面提到获取配置信息
的最小粒度是Plugin
。更配置信息
的最小粒度是配置项。
handlePost 提供六种操作,分四大类:设置、还原配置项;更新、删除Plugin。
public static final String SET_PROPERTY = "set-property";
public static final String UNSET_PROPERTY = "unset-property";
public static final String SET_USER_PROPERTY = "set-user-property";
public static final String UNSET_USER_PROPERTY = "unset-user-property";
public static final String SET = "set";
public static final String UPDATE = "update";
public static final String CREATE = "create";
- 操作配置项
- set 更改solrcofig配置
- unset 还原更改
- unset-property 等同于 unset-user-property;set-property 等同于 set-user-property
从语义上,user-property是指 configoverlay.json;property是指 solrconfig.xml。但是呢,我们上面已经讲过了,solrconfig.xml并不能直接修改,修改的都是configoverlay.json。因为 user-property和property实际上是一回事。
尽管有很多时候都只是语义上的差异,但我个人还是建议遵守语义的差异
- 操作Plugin配置
- set/create/update 其实一回事,只是语义的不同,逻辑一样的。
- delete 和 update 顾名思义,但是只能操作整个plugin。
这里也是我觉得SolrConfigHandler比较新颖和神奇的地方,就是她接受的参数是一个JSON,格式上跟我们METHOD.GET
时返回结果格式基本一致。
{'set-property':{'updateHandler.autoCommit.maxTime':-1}}
{'unset-property':'updateHandler.autoCommit.maxTime'}
注:METHOD.POST
时,参数放在Post Body中