Solr 6.0 學習(十四)Solr RequstHandler

什麼是RequstHandler

我們看到solrconfig.xml中配置

<requestHandler name="/select" class="solr.SearchHandler">

結合solr admin管理後臺我們看到
這裏寫圖片描述
第一個qt參數是select,那麼solr對應的requestHandler是SearchHandler

RequstHandler做了什麼

下面我們通過solr源碼的代碼段來展示。
在之前的文章 Solr 6.0 學習(九) SolrDispatchFilter源碼解析之HttpSolrCall及擴展 中我們已經看到了訪問solr的整個過程脈絡,其中有如下代碼段:

this.handler = this.cores.getRequestHandler(this.path);

我們找到源碼CoreContainer類下的方法:

public SolrRequestHandler getRequestHandler(final String path) {
        return RequestHandlerBase.getRequestHandler(path, this.containerHandlers);
    }

RequestHandlerBase中代碼如下:

public static SolrRequestHandler getRequestHandler(final String handlerName, final PluginBag<SolrRequestHandler> reqHandlers) {
        if (handlerName == null) {
            return null;
        }
        SolrRequestHandler handler = reqHandlers.get(handlerName);
        int idx = 0;
        if (handler == null) {
            while (true) {
                idx = handlerName.indexOf(47, idx + 1);
                if (idx <= 0) {
                    break;
                }
                final String firstPart = handlerName.substring(0, idx);
                handler = reqHandlers.get(firstPart);
                if (handler == null) {
                    continue;
                }
                if (handler instanceof NestedRequestHandler) {
                    return ((NestedRequestHandler)handler).getSubHandler(handlerName.substring(idx));
                }
            }
        }
        return handler;
    }

實際上以上代碼就是獲取到當前訪問qt參數對應的requestHandler。

接下來我們看到 Solr 6.0 學習(九) SolrDispatchFilter源碼解析之HttpSolrCall及擴展

/**
 * 執行查詢
 * @param rsp
 */
  protected void execute(SolrQueryResponse rsp)
  {
    this.solrReq.getContext().put("webapp", this.req.getContextPath());
    this.solrReq.getCore().execute(this.handler, this.solrReq, rsp);
  }

我們找到SolrCore源碼發現

public void execute(final SolrRequestHandler handler, final SolrQueryRequest req, final SolrQueryResponse rsp) {
        if (handler == null) {
            final String msg = "Null Request Handler '" + req.getParams().get("qt") + "'";
            if (SolrCore.log.isWarnEnabled()) {
                SolrCore.log.warn(this.logid + msg + ":" + req);
            }
            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
        }
        preDecorateResponse(req, rsp);
        if (SolrCore.requestLog.isDebugEnabled() && rsp.getToLog().size() > 0) {
            SolrCore.requestLog.debug(rsp.getToLogAsString(this.logid));
        }
        handler.handleRequest(req, rsp);
        postDecorateResponse(handler, req, rsp);
        if (rsp.getToLog().size() > 0) {
            if (SolrCore.requestLog.isInfoEnabled()) {
                SolrCore.requestLog.info(rsp.getToLogAsString(this.logid));
            }
            if (SolrCore.log.isWarnEnabled() && this.slowQueryThresholdMillis >= 0) {
                final long qtime = (long)req.getRequestTimer().getTime();
                if (qtime >= this.slowQueryThresholdMillis) {
                    SolrCore.log.warn("slow: " + rsp.getToLogAsString(this.logid));
                }
            }
        }
    }

handler.handleRequest(req, rsp)處理器對當前請求做處理,從之前的配置我們知道當前請求的處理器是SearchHandler

package org.apache.solr.handler.component;

import org.apache.solr.handler.*;
import org.apache.solr.util.plugin.*;
import org.apache.solr.request.*;
import org.apache.solr.core.*;
import org.apache.solr.cloud.*;
import org.apache.solr.response.*;
import org.apache.solr.search.*;
import org.apache.solr.common.*;
import org.apache.lucene.index.*;
import java.util.*;
import org.apache.solr.common.params.*;
import org.apache.lucene.util.*;
import org.apache.solr.common.util.*;
import org.apache.solr.client.solrj.*;
import java.io.*;
import org.apache.solr.util.*;
import java.lang.invoke.*;
import org.slf4j.*;

public class SearchHandler extends RequestHandlerBase implements SolrCoreAware, PluginInfoInitialized
{
    static final String INIT_COMPONENTS = "components";
    static final String INIT_FIRST_COMPONENTS = "first-components";
    static final String INIT_LAST_COMPONENTS = "last-components";
    private static final Logger log;
    protected volatile List<SearchComponent> components;
    private ShardHandlerFactory shardHandlerFactory;
    private PluginInfo shfInfo;
    private SolrCore core;

    /*
     *獲取默認查詢組件
     */
    protected List<String> getDefaultComponents() {
        final ArrayList<String> names = new ArrayList<String>(8);
        names.add("query");
        names.add("facet");
        names.add("facet_module");
        names.add("mlt");
        names.add("highlight");
        names.add("stats");
        names.add("debug");
        names.add("expand");
        return names;
    }

    @Override
    public void init(final PluginInfo info) {
        this.init(info.initArgs);
        for (final PluginInfo child : info.children) {
            if ("shardHandlerFactory".equals(child.type)) {
                this.shfInfo = child;
                break;
            }
        }
    }

    @Override
    public void inform(final SolrCore core) {
        this.core = core;
        final Set<String> missing = new HashSet<String>();
        final List<String> c = (List<String>)this.initArgs.get("components");
        missing.addAll(core.getSearchComponents().checkContains(c));
        final List<String> first = (List<String>)this.initArgs.get("first-components");
        missing.addAll(core.getSearchComponents().checkContains(first));
        final List<String> last = (List<String>)this.initArgs.get("last-components");
        missing.addAll(core.getSearchComponents().checkContains(last));
        if (!missing.isEmpty()) {
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Missing SearchComponents named : " + missing);
        }
        if (c != null && (first != null || last != null)) {
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "First/Last components only valid if you do not declare 'components'");
        }
        if (this.shfInfo == null) {
            this.shardHandlerFactory = core.getCoreDescriptor().getCoreContainer().getShardHandlerFactory();
        }
        else {
            this.shardHandlerFactory = core.createInitInstance(this.shfInfo, ShardHandlerFactory.class, null, null);
            core.addCloseHook(new CloseHook() {
                @Override
                public void preClose(final SolrCore core) {
                    SearchHandler.this.shardHandlerFactory.close();
                }

                @Override
                public void postClose(final SolrCore core) {
                }
            });
        }
    }

    /*
     * 初始化配置的查詢組件
     */
    private void initComponents() {
        final Object declaredComponents = this.initArgs.get("components");
        final List<String> first = (List<String>)this.initArgs.get("first-components");
        final List<String> last = (List<String>)this.initArgs.get("last-components");
        List<String> list = null;
        boolean makeDebugLast = true;
        if (declaredComponents == null) {
            list = this.getDefaultComponents();
            if (first != null) {
                final List<String> clist = first;
                clist.addAll(list);
                list = clist;
            }
            if (last != null) {
                list.addAll(last);
            }
        }
        else {
            list = (List<String>)declaredComponents;
            if (first != null || last != null) {
                throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "First/Last components only valid if you do not declare 'components'");
            }
            makeDebugLast = false;
        }
        final List<SearchComponent> components = new ArrayList<SearchComponent>(list.size());
        DebugComponent dbgCmp = null;
        for (final String c : list) {
            final SearchComponent comp = this.core.getSearchComponent(c);
            if (comp instanceof DebugComponent && makeDebugLast) {
                dbgCmp = (DebugComponent)comp;
            }
            else {
                components.add(comp);
                SearchHandler.log.debug("Adding  component:" + comp);
            }
        }
        if (makeDebugLast && dbgCmp != null) {
            components.add(dbgCmp);
            SearchHandler.log.debug("Adding  debug component:" + dbgCmp);
        }
        this.components = components;
    }

    /*
     * 獲取查詢組件
     */
    public List<SearchComponent> getComponents() {
        List<SearchComponent> result = this.components;
        if (result == null) {
            synchronized (this) {
                if (this.components == null) {
                    this.initComponents();
                }
                result = this.components;
            }
        }
        return result;
    }

    private ShardHandler getAndPrepShardHandler(final SolrQueryRequest req, final ResponseBuilder rb) {
        ShardHandler shardHandler = null;
        final CoreContainer cc = req.getCore().getCoreDescriptor().getCoreContainer();
        final boolean isZkAware = cc.isZooKeeperAware();
        if (!(rb.isDistrib = req.getParams().getBool("distrib", isZkAware))) {
            final String shards = req.getParams().get("shards");
            rb.isDistrib = (shards != null && shards.indexOf(47) > 0);
        }
        if (rb.isDistrib) {
            shardHandler = this.shardHandlerFactory.getShardHandler();
            shardHandler.prepDistributed(rb);
            if (!rb.isDistrib) {
                shardHandler = null;
            }
        }
        if (isZkAware) {
            final ZkController zkController = cc.getZkController();
            final NamedList<Object> headers = rb.rsp.getResponseHeader();
            if (headers != null) {
                headers.add("zkConnected", (Object)(zkController != null && !zkController.getZkClient().getConnectionManager().isLikelyExpired()));
            }
        }
        return shardHandler;
    }

    @Override
    public void handleRequestBody(final SolrQueryRequest req, final SolrQueryResponse rsp) throws Exception {
        final List<SearchComponent> components = this.getComponents();
        final ResponseBuilder rb = new ResponseBuilder(req, rsp, components);
        if (rb.requestInfo != null) {
            rb.requestInfo.setResponseBuilder(rb);
        }
        final boolean dbg = req.getParams().getBool("debugQuery", false);
        rb.setDebug(dbg);
        if (!dbg) {
            SolrPluginUtils.getDebugInterests(req.getParams().getParams("debug"), rb);
        }
        final RTimerTree timer = rb.isDebug() ? req.getRequestTimer() : null;
        final ShardHandler shardHandler1 = this.getAndPrepShardHandler(req, rb);
        if (timer == null) {
            for (final SearchComponent c : components) {
                c.prepare(rb);
            }
        }
        else {
            final RTimerTree subt = timer.sub("prepare");
            for (final SearchComponent c2 : components) {
                rb.setTimer(subt.sub(c2.getName()));
                c2.prepare(rb);
                rb.getTimer().stop();
            }
            subt.stop();
        }
        if (!rb.isDistrib) {
            final long timeAllowed = req.getParams().getLong("timeAllowed", -1L);
            if (timeAllowed > 0L) {
                SolrQueryTimeoutImpl.set(timeAllowed);
            }
            try {
                if (!rb.isDebug()) {
                    for (final SearchComponent c3 : components) {
                        c3.process(rb);
                    }
                }
                else {
                    final RTimerTree subt2 = timer.sub("process");
                    for (final SearchComponent c4 : components) {
                        rb.setTimer(subt2.sub(c4.getName()));
                        c4.process(rb);
                        rb.getTimer().stop();
                    }
                    subt2.stop();
                    if (rb.isDebugTimings()) {
                        rb.addDebugInfo("timing", timer.asNamedList());
                    }
                }
            }
            catch (ExitableDirectoryReader.ExitingReaderException ex) {
                SearchHandler.log.warn("Query: " + req.getParamString() + "; " + ex.getMessage());
                SolrDocumentList r = (SolrDocumentList)rb.rsp.getResponse();
                if (r == null) {
                    r = new SolrDocumentList();
                }
                r.setNumFound(0L);
                rb.rsp.addResponse(r);
                if (rb.isDebug()) {
                    final NamedList debug = new NamedList();
                    debug.add("explain", (Object)new NamedList());
                    rb.rsp.add("debug", debug);
                }
                rb.rsp.getResponseHeader().add("partialResults", (Object)Boolean.TRUE);
            }
            finally {
                SolrQueryTimeoutImpl.reset();
            }
        }
        else {
            if (rb.outgoing == null) {
                rb.outgoing = new LinkedList<ShardRequest>();
            }
            rb.finished = new ArrayList<ShardRequest>();
            int nextStage = 0;
            do {
                rb.stage = nextStage;
                nextStage = ResponseBuilder.STAGE_DONE;
                for (final SearchComponent c2 : components) {
                    nextStage = Math.min(nextStage, c2.distributedProcess(rb));
                }
                while (rb.outgoing.size() > 0) {
                    while (rb.outgoing.size() > 0) {
                        final ShardRequest sreq = rb.outgoing.remove(0);
                        sreq.actualShards = sreq.shards;
                        if (sreq.actualShards == ShardRequest.ALL_SHARDS) {
                            sreq.actualShards = rb.shards;
                        }
                        sreq.responses = new ArrayList<ShardResponse>(sreq.actualShards.length);
                        for (final String shard : sreq.actualShards) {
                            final ModifiableSolrParams params = new ModifiableSolrParams((SolrParams)sreq.params);
                            params.remove("shards");
                            params.set("distrib", new String[] { "false" });
                            params.remove("indent");
                            params.remove("echoParams");
                            params.set("isShard", true);
                            params.set("shards.purpose", sreq.purpose);
                            params.set("shard.url", new String[] { shard });
                            if (rb.requestInfo != null) {
                                params.set("NOW", new String[] { Long.toString(rb.requestInfo.getNOW().getTime()) });
                            }
                            final String shardQt = params.get("shards.qt");
                            if (shardQt != null) {
                                params.set("qt", new String[] { shardQt });
                            }
                            else if (req.getCore().getSolrConfig().luceneMatchVersion.onOrAfter(Version.LUCENE_5_1_0)) {
                                final String reqPath = req.getContext().get("path");
                                if (!"/select".equals(reqPath)) {
                                    params.set("qt", new String[] { reqPath });
                                }
                            }
                            else {
                                params.remove("qt");
                            }
                            shardHandler1.submit(sreq, shard, params, rb.preferredHostAddress);
                        }
                    }
                    final boolean tolerant = rb.req.getParams().getBool("shards.tolerant", false);
                    while (rb.outgoing.size() == 0) {
                        final ShardResponse srsp = tolerant ? shardHandler1.takeCompletedIncludingErrors() : shardHandler1.takeCompletedOrError();
                        if (srsp == null) {
                            break;
                        }
                        if (srsp.getException() != null) {
                            if (!tolerant) {
                                shardHandler1.cancelAll();
                                if (srsp.getException() instanceof SolrException) {
                                    throw (SolrException)srsp.getException();
                                }
                                throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, srsp.getException());
                            }
                            else if (rsp.getResponseHeader().get("partialResults") == null) {
                                rsp.getResponseHeader().add("partialResults", (Object)Boolean.TRUE);
                            }
                        }
                        rb.finished.add(srsp.getShardRequest());
                        for (final SearchComponent c4 : components) {
                            c4.handleResponses(rb, srsp.getShardRequest());
                        }
                    }
                }
                for (final SearchComponent c2 : components) {
                    c2.finishStage(rb);
                }
            } while (nextStage != Integer.MAX_VALUE);
        }
        if (!rb.isDistrib && req.getParams().getBool("shards.info", false) && rb.shortCircuitedURL != null) {
            final NamedList<Object> shardInfo = (NamedList<Object>)new SimpleOrderedMap();
            final SimpleOrderedMap<Object> nl = (SimpleOrderedMap<Object>)new SimpleOrderedMap();
            if (rsp.getException() != null) {
                Throwable cause = rsp.getException();
                if (cause instanceof SolrServerException) {
                    cause = ((SolrServerException)cause).getRootCause();
                }
                else if (cause.getCause() != null) {
                    cause = cause.getCause();
                }
                nl.add("error", (Object)cause.toString());
                final StringWriter trace = new StringWriter();
                cause.printStackTrace(new PrintWriter(trace));
                nl.add("trace", (Object)trace.toString());
            }
            else {
                nl.add("numFound", (Object)rb.getResults().docList.matches());
                nl.add("maxScore", (Object)rb.getResults().docList.maxScore());
            }
            nl.add("shardAddress", (Object)rb.shortCircuitedURL);
            nl.add("time", (Object)req.getRequestTimer().getTime());
            final int pos = rb.shortCircuitedURL.indexOf("://");
            final String shardInfoName = (pos != -1) ? rb.shortCircuitedURL.substring(pos + 3) : rb.shortCircuitedURL;
            shardInfo.add(shardInfoName, (Object)nl);
            rsp.getValues().add("shards.info", (Object)shardInfo);
        }
    }

    @Override
    public String getDescription() {
        final StringBuilder sb = new StringBuilder();
        sb.append("Search using components: ");
        if (this.components != null) {
            for (final SearchComponent c : this.components) {
                sb.append(c.getName());
                sb.append(",");
            }
        }
        return sb.toString();
    }

    static {
        log = LoggerFactory.getLogger((Class)MethodHandles.lookup().lookupClass());
    }
}

我們發現沒有找到handleRequest,實際上是因爲searchRequest繼承了RequestHandlerBase,RequestHandlerBase中

public void handleRequest(final SolrQueryRequest req, final SolrQueryResponse rsp) {
        this.numRequests.incrementAndGet();
        final TimerContext timer = this.requestTimes.time();
        try {
            if (this.pluginInfo != null && this.pluginInfo.attributes.containsKey("useParams")) {
                req.getContext().put("useParams", this.pluginInfo.attributes.get("useParams"));
            }
            SolrPluginUtils.setDefaults(this, req, this.defaults, this.appends, this.invariants);
            req.getContext().remove("useParams");
            rsp.setHttpCaching(this.httpCaching);
            this.handleRequestBody(req, rsp);
            final NamedList header = rsp.getResponseHeader();
            if (header != null) {
                final Object partialResults = header.get("partialResults");
                final boolean timedOut = partialResults != null && (boolean)partialResults;
                if (timedOut) {
                    this.numTimeouts.incrementAndGet();
                    rsp.setHttpCaching(false);
                }
            }
        }
        catch (Exception e) {
            if (e instanceof SolrException) {
                final SolrException se = (SolrException)e;
                if (se.code() != SolrException.ErrorCode.CONFLICT.code) {
                    SolrException.log(RequestHandlerBase.log, (Throwable)e);
                }
            }
            else {
                SolrException.log(RequestHandlerBase.log, (Throwable)e);
                if (e instanceof SyntaxError) {
                    e = (Exception)new SolrException(SolrException.ErrorCode.BAD_REQUEST, (Throwable)e);
                }
            }
            rsp.setException(e);
            this.numErrors.incrementAndGet();
        }
        finally {
            timer.stop();
        }
    }

可以看到最終是調用了handleRequestBody這個方法。
handleRequestBody這個方法實際上就是組裝查詢組件。實際上這也就是RequstHandler的核心作用。

總結

RequstHandler是查詢請求處理器。支持自定義。

發佈了84 篇原創文章 · 獲贊 49 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章