什麼是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是查詢請求處理器。支持自定義。