Solr/SolrCloud SolrConfigHandler详解

这一个是比较神奇的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";
  • 操作配置项
    1. set 更改solrcofig配置
    2. unset 还原更改
    3. 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配置
    1. set/create/update 其实一回事,只是语义的不同,逻辑一样的。
    2. delete 和 update 顾名思义,但是只能操作整个plugin。

这里也是我觉得SolrConfigHandler比较新颖和神奇的地方,就是她接受的参数是一个JSON,格式上跟我们METHOD.GET时返回结果格式基本一致。

{'set-property':{'updateHandler.autoCommit.maxTime':-1}}
{'unset-property':'updateHandler.autoCommit.maxTime'}

注:METHOD.POST时,参数放在Post Body中

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章