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中

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