這一個是比較神奇的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中