solr源码分析—doFilter
本章开始分析SolrDispatchFilter的doFilter函数,该函数被tomcat等框架调用。继续申明一下,为了方便分析和查看,文章的代码省略了一些不重要的部分。
SolrDispatchFilter::doFilter
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain, boolean retry) throws IOException, ServletException {
...
HttpSolrCall call = getHttpSolrCall((HttpServletRequest) request, (HttpServletResponse) response, retry);
ExecutorUtil.setServerThreadFlag(Boolean.TRUE);
Action result = call.call();
switch (result) {
case PASSTHROUGH:
chain.doFilter(request, response);
break;
case RETRY:
doFilter(request, response, chain, true);
break;
case FORWARD:
request.getRequestDispatcher(call.getPath()).forward(request, response);
break;
}
...
}
doFilter首先调用getHttpSolrCall创建HttpSolrCall,HttpSolrCall的call函数根据请求路径获取相应的处理类进行处理,最后返回处理结果。
HttpSolrCall的构造函数很简单,往下看其call函数。
SolrDispatchFilter::doFilter->HttpSolrCall::call
public Action call() throws IOException {
init();
HttpServletResponse resp = response;
switch (action) {
case ADMIN:
handleAdminRequest();
return RETURN;
...
}
...
}
init函数根据请求路径获取对应的处理类Handler,并将请求HttpServletRequest封装成一个SolrQueryRequest。下面假设请求路径在admin下,此时action为Admin,因此通过handleAdminRequest函数处理admin路径下的请求。
init
SolrDispatchFilter::doFilter->HttpSolrCall::call->init
private void init() throws Exception {
Aliases aliases = null;
String corename = "";
String origCorename = null;
req.setAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE, new RTimerTree());
req.setAttribute("org.apache.solr.CoreContainer", cores);
path = req.getServletPath();
int idx = path.indexOf(':');
boolean usingAliases = false;
handler = cores.getRequestHandler(path);
if (handler != null) {
solrReq = SolrRequestParsers.DEFAULT.parse(null, path, req);
solrReq.getContext().put(CoreContainer.class.getName(), cores);
requestType = RequestType.ADMIN;
action = ADMIN;
return;
} else {
...
}
...
}
getRequestHandler根据请求路径path获取对应的Handler。DEFAULT默认为SolrRequestParsers,其parse函数将请求HttpServletRequest提取出有效信息封装成一个SolrQueryRequest,然后向其添加相当于环境信息CoreContainer并返回。
SolrDispatchFilter::doFilter->HttpSolrCall::call->init->CoreContainer::getRequestHandler
public SolrRequestHandler getRequestHandler(String path) {
return RequestHandlerBase.getRequestHandler(path, containerHandlers);
}
public static SolrRequestHandler getRequestHandler(String handlerName, PluginBag<SolrRequestHandler> reqHandlers) {
if(handlerName == null) return null;
SolrRequestHandler handler = reqHandlers.get(handlerName);
int idx = 0;
if(handler == null) {
for (; ; ) {
idx = handlerName.indexOf('/', idx+1);
if (idx > 0) {
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));
}
} else {
break;
}
}
}
return handler;
}
getRequestHandler根据请求路径path获取对应的处理类Handler,这里假设路径为/admin/core,则获取到CoreAdminHandler。假设一开始在reqHandlers中没有获取到Handler,则可能路径过长,这时就从左往右截取路径,直到找到对应的Handler返回,或者继续通过getSubHandler获取子Handler。
SolrDispatchFilter::doFilter->HttpSolrCall::call->init->SolrRequestParsers::parse
public SolrQueryRequest parse( SolrCore core, String path, HttpServletRequest req ) throws Exception{
SolrRequestParser parser = standard;
ArrayList<ContentStream> streams = new ArrayList<>(1);
SolrParams params = parser.parseParamsAndFillStreams( req, streams );
SolrQueryRequest sreq = buildRequestFrom(core, params, streams, getRequestTimer(req), req);
sreq.getContext().put(PATH, RequestHandlers.normalize(path));
sreq.getContext().put("httpMethod", req.getMethod());
return sreq;
}
这里的standard是StandardRequestParser,其parseParamsAndFillStreams函数解析request中的参数存入map结构params中。buildRequestFrom函数根据请求信息params创建SolrQueryRequest,然后向其添加请求路径信息和请求方法信息,最后返回。
SolrDispatchFilter::doFilter->HttpSolrCall::call->init->SolrRequestParsers::parse->buildRequestFrom
private SolrQueryRequest buildRequestFrom(SolrCore core, SolrParams params, Collection<ContentStream> streams, RTimerTree requestTimer, final HttpServletRequest req) throws Exception {
...
SolrQueryRequestBase q = new SolrQueryRequestBase(core, params, requestTimer) {
@Override
public Principal getUserPrincipal() {
return req == null ? null : req.getUserPrincipal();
}
};
return q;
}
buildRequestFrom创建一个SolrQueryRequestBase封装请求信息并返回。
假设请求路径在admin下,根据前面的分析,solr会调用handleAdminRequest函数处理这部分请求。
SolrDispatchFilter::doFilter->HttpSolrCall::call->handleAdminRequest
private void handleAdminRequest() throws IOException {
SolrQueryResponse solrResp = new SolrQueryResponse();
SolrCore.preDecorateResponse(solrReq, solrResp);
handler.handleRequest(solrReq, solrResp);
SolrCore.postDecorateResponse(handler, solrReq, solrResp);
QueryResponseWriter respWriter = SolrCore.DEFAULT_RESPONSE_WRITERS.get(solrReq.getParams().get(CommonParams.WT));
if (respWriter == null) respWriter = SolrCore.DEFAULT_RESPONSE_WRITERS.get("standard");
writeResponse(solrResp, respWriter, Method.getMethod(req.getMethod()));
}
preDecorateResponse向SolrQueryResponse中添加头部结构,并生成一些日志方面的信息。
假设这里的handler为CoreAdminHandler,handleRequest函数是处理请求的主要函数。
postDecorateResponse向SolrQueryResponse的头部添加信息,例如请求处理是否成功,处理时间等。
再往下根据请求参数获得QueryResponseWriter,假设需要获得的是json返回结果,则获得JsonResponseWriter,默认“standard”会获得XMLResponseWriter。最后调用writeResponse写入返回信息。
SolrDispatchFilter::doFilter->HttpSolrCall::call->handleAdminRequest->CoreAdminHandler::handleRequest
public void handleRequest(SolrQueryRequest req, SolrQueryResponse rsp) {
...
req.getContext().remove(USEPARAM);
rsp.setHttpCaching(httpCaching);
handleRequestBody( req, rsp );
...
}
handleRequest进而通过handleRequestBody处理请求。
SolrDispatchFilter::doFilter->HttpSolrCall::call->handleAdminRequest->CoreAdminHandler::handleRequest->handleRequestBody
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
...
CoreAdminOperation op = opMap.get(req.getParams().get(ACTION, STATUS.toString()).toLowerCase(Locale.ROOT));
final CallInfo callInfo = new CallInfo(this, req, rsp, op);
callInfo.call();
}
void call() throws Exception {
op.call(this);
}
根据请求参数的ACTION的值从opMap中获取CoreAdminOperation,封装成CallInfo并调用其call函数进行处理。CoreAdminHandler在创建时会执行静态代码,
static {
for (CoreAdminOperation op : CoreAdminOperation.values())
opMap.put(op.action.toString().toLowerCase(Locale.ROOT), op);
}
到这里就不往下看,下一章开始再往下看具体的处理函数。
SolrDispatchFilter::doFilter->HttpSolrCall::call->handleAdminRequest->writeResponse
private void writeResponse(SolrQueryResponse solrRsp, QueryResponseWriter responseWriter, Method reqMethod)
throws IOException {
final String ct = responseWriter.getContentType(solrReq, solrRsp);
response.setContentType(ct);
OutputStream out = new CloseShieldOutputStream(response.getOutputStream());
QueryResponseWriterUtil.writeQueryResponse(out, responseWriter, solrReq, solrRsp, ct);
}
首先通过getContentType获得ContentType,设置进response中,然后调用QueryResponseWriterUtil的writeQueryResponse函数写入返回信息。
SolrDispatchFilter::doFilter->HttpSolrCall::call->handleAdminRequest->writeResponse->QueryResponseWriterUtil::writeQueryResponse
public static void writeQueryResponse(OutputStream outputStream,
QueryResponseWriter responseWriter, SolrQueryRequest solrRequest,
SolrQueryResponse solrResponse, String contentType) throws IOException {
OutputStream out = new OutputStream() {
@Override
public void write(int b) throws IOException {
outputStream.write(b);
}
@Override
public void flush() throws IOException {
}
};
Writer writer = buildWriter(out, ContentStreamBase.getCharsetFromContentType(contentType));
responseWriter.write(writer, solrRequest, solrResponse);
writer.flush();
}
buildWriter创建FastWriter,内部封装了根据编码类型创建的输出流OutputStreamWriter。接下来调用JSONResponseWriter的write进行写。最后缓存保存的是JSON格式的返回信息,将数据flush出去。
SolrDispatchFilter::doFilter->HttpSolrCall::call->handleAdminRequest->writeResponse->QueryResponseWriterUtil::writeQueryResponse->
public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
JSONWriter w = new JSONWriter(writer, req, rsp);
w.writeResponse();
w.close();
}
public void writeResponse() throws IOException {
writeNamedList(null, rsp.getValues());
if(wrapperFunction!=null) {
writer.write(')');
}
writer.write('\n');
}
writeNamedList函数将map结构的返回信息写成JSON格式并返回,这里就不往下看了。