Solr 6.0 学习(八) SolrDispatchFilter源码解析及solr扩展

来源:互联网 发布:淘宝折扣代购 编辑:程序博客网 时间:2024/06/01 20:37

传送门:老版SolrDispatchFilter源码解析

SolrDispatchFilter做了什么

我们发布好我们的solr6.X之后我们可以看到项目下web.xml中一段配置

<!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->  <filter>    <filter-name>SolrRequestFilter</filter-name>    <filter-class>org.apache.solr.servlet.SolrDispatchFilter</filter-class>    <!--    Exclude patterns is a list of directories that would be short circuited by the     SolrDispatchFilter. It includes all Admin UI related static content.    NOTE: It is NOT a pattern but only matches the start of the HTTP ServletPath.    -->    <init-param>      <param-name>excludePatterns</param-name>      <param-value>/css/.+,/js/.+,/img/.+,/tpl/.+</param-value>    </init-param>  </filter>  <filter-mapping>    <!--      NOTE: When using multicore, /admin JSP URLs with a core specified      such as /solr/coreName/admin/stats.jsp get forwarded by a      RequestDispatcher to /solr/admin/stats.jsp with the specified core      put into request scope keyed as "org.apache.solr.SolrCore".      It is unnecessary, and potentially problematic, to have the SolrDispatchFilter      configured to also filter on forwards.  Do not configure      this dispatcher as <dispatcher>FORWARD</dispatcher>.    -->    <filter-name>SolrRequestFilter</filter-name>    <url-pattern>/*</url-pattern>  </filter-mapping>

我们注意其中org.apache.solr.servlet.SolrDispatchFilter
这个是个全局的过滤器Filter。下面我们来一步步的研究一下其源码主要做了些什么

BaseSolrFilter

package org.apache.solr.servlet;import javax.servlet.Filter;abstract class BaseSolrFilter  implements Filter{  static  {    CheckLoggingConfiguration.check();  }}

CheckLoggingConfiguration

package org.apache.solr.servlet;import org.slf4j.LoggerFactory;final class CheckLoggingConfiguration{  static void check()  {    try    {      LoggerFactory.getLogger(CheckLoggingConfiguration.class);    } catch (NoClassDefFoundError e) {      throw new NoClassDefFoundError("Failed to initialize Apache Solr: Could not find necessary SLF4j logging jars. If using Jetty, the SLF4j logging jars need to go in the jetty lib/ext directory. For other containers, the corresponding directory should be used. For more information, see: http://wiki.apache.org/solr/SolrLogging");    }  }}

SolrDispatchFilter

package org.apache.solr.servlet;import java.io.ByteArrayInputStream;import java.io.IOException;import java.lang.invoke.MethodHandles;import java.lang.invoke.MethodHandles.Lookup;import java.nio.file.Path;import java.nio.file.Paths;import java.util.ArrayList;import java.util.Properties;import java.util.concurrent.atomic.AtomicBoolean;import java.util.concurrent.atomic.AtomicReference;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.RequestDispatcher;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletInputStream;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.lang.StringUtils;import org.apache.http.client.HttpClient;import org.apache.solr.common.SolrException;import org.apache.solr.common.SolrException.ErrorCode;import org.apache.solr.common.cloud.SolrZkClient;import org.apache.solr.common.util.ExecutorUtil;import org.apache.solr.core.CoreContainer;import org.apache.solr.core.NodeConfig;import org.apache.solr.core.SolrCore;import org.apache.solr.core.SolrResourceLoader;import org.apache.solr.core.SolrXmlConfig;import org.apache.solr.request.SolrRequestInfo;import org.apache.solr.security.AuthenticationPlugin;import org.apache.solr.update.UpdateShardHandler;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class SolrDispatchFilter extends BaseSolrFilter{  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());  protected volatile CoreContainer cores;  protected String abortErrorMessage = null;  protected HttpClient httpClient;  private ArrayList<Pattern> excludePatterns;  public static final String PROPERTIES_ATTRIBUTE = "solr.properties";  public static final String SOLRHOME_ATTRIBUTE = "solr.solr.home";  public void init(FilterConfig config)    throws ServletException  {    log.info("SolrDispatchFilter.init(): {}", getClass().getClassLoader());    //获取filter配置中的excludePatterns,实际上就是配置不经过filter的路径    String exclude = config.getInitParameter("excludePatterns");    if (exclude != null) {      String[] excludeArray = exclude.split(",");      this.excludePatterns = new ArrayList();      for (String element : excludeArray)        this.excludePatterns.add(Pattern.compile(element));    }    try    {      Properties extraProperties = (Properties)config.getServletContext().getAttribute("solr.properties");      if (extraProperties == null) {        extraProperties = new Properties();      }      String solrHome = (String)config.getServletContext().getAttribute("solr.solr.home");      ExecutorUtil.addThreadLocalProvider(SolrRequestInfo.getInheritableThreadLocalProvider());      this.cores = createCoreContainer(solrHome == null ? SolrResourceLoader.locateSolrHome() : Paths.get(solrHome, new String[0]), extraProperties);      this.httpClient = this.cores.getUpdateShardHandler().getHttpClient();      log.info("user.dir=" + System.getProperty("user.dir"));    }    catch (Throwable t)    {      log.error("Could not start Solr. Check solr/home property and the logs");      SolrCore.log(t);      if ((t instanceof Error)) {        throw ((Error)t);      }    }    log.info("SolrDispatchFilter.init() done");  }  protected CoreContainer createCoreContainer(Path solrHome, Properties extraProperties)  {    NodeConfig nodeConfig = loadNodeConfig(solrHome, extraProperties);    this.cores = new CoreContainer(nodeConfig, extraProperties, true);    this.cores.load();    return this.cores;  }  public static NodeConfig loadNodeConfig(Path solrHome, Properties nodeProperties)  {    SolrResourceLoader loader = new SolrResourceLoader(solrHome, null, nodeProperties);    if (!StringUtils.isEmpty(System.getProperty("solr.solrxml.location"))) {      log.warn("Solr property solr.solrxml.location is no longer supported. Will automatically load solr.xml from ZooKeeper if it exists");    }    String zkHost = System.getProperty("zkHost");    if (!StringUtils.isEmpty(zkHost)) {      try { SolrZkClient zkClient = new SolrZkClient(zkHost, 30000); Throwable localThrowable4 = null;        try { if (zkClient.exists("/solr.xml", true).booleanValue()) {            log.info("solr.xml found in ZooKeeper. Loading...");            byte[] data = zkClient.getData("/solr.xml", null, null, true);            return SolrXmlConfig.fromInputStream(loader, new ByteArrayInputStream(data));          }        }        catch (Throwable localThrowable2)        {          localThrowable4 = localThrowable2; throw localThrowable2;        }        finally        {          if (zkClient != null) if (localThrowable4 != null) try { zkClient.close(); } catch (Throwable localThrowable3) { localThrowable4.addSuppressed(localThrowable3); } else zkClient.close();          } } catch (Exception e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error occurred while loading solr.xml from zookeeper", e); }      log.info("Loading solr.xml from SolrHome (not found in ZooKeeper)");    }    return SolrXmlConfig.fromSolrHome(loader, loader.getInstancePath());  }  public CoreContainer getCores() {    return this.cores;  }  public void destroy()  {    if (this.cores != null)      try {        this.cores.shutdown();        this.cores = null; } finally { this.cores = null; }  }  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)    throws IOException, ServletException  {    doFilter(request, response, chain, false);  }  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain, boolean retry) throws IOException, ServletException {    if (!(request instanceof HttpServletRequest)) return;    try    {      if ((this.cores == null) || (this.cores.isShutDown())) {        log.error("Error processing the request. CoreContainer is either not initialized or shutting down.");        throw new SolrException(SolrException.ErrorCode.SERVICE_UNAVAILABLE, "Error processing the request. CoreContainer is either not initialized or shutting down.");      }      AtomicReference wrappedRequest = new AtomicReference();      if (!authenticateRequest(request, response, wrappedRequest))      {        return;      }      if (wrappedRequest.get() != null) {        request = (ServletRequest)wrappedRequest.get();      }      if (this.cores.getAuthenticationPlugin() != null)        log.debug("User principal: {}", ((HttpServletRequest)request).getUserPrincipal());      String requestPath;      if (this.excludePatterns != null) {        requestPath = ((HttpServletRequest)request).getServletPath();        String extraPath = ((HttpServletRequest)request).getPathInfo();        if (extraPath != null)        {          requestPath = requestPath + extraPath;        }        for (Pattern p : this.excludePatterns) {          Matcher matcher = p.matcher(requestPath);          if (matcher.lookingAt()) {            chain.doFilter(request, response);            return;          }        }      }      HttpSolrCall call = getHttpSolrCall((HttpServletRequest)request, (HttpServletResponse)response, retry);      ExecutorUtil.setServerThreadFlag(Boolean.TRUE);      try {        Action result = call.call();        switch (2.$SwitchMap$org$apache$solr$servlet$SolrDispatchFilter$Action[result.ordinal()]) {        case 1:          chain.doFilter(request, response);          break;        case 2:          doFilter(request, response, chain, true);          break;        case 3:          request.getRequestDispatcher(call.getPath()).forward(request, response);        }      }      finally {        call.destroy();        ExecutorUtil.setServerThreadFlag(null);      }    } finally {      consumeInputFully((HttpServletRequest)request);    }  }  private void consumeInputFully(HttpServletRequest req)  {    try {      ServletInputStream is = req.getInputStream();      while ((!is.isFinished()) && (is.read() != -1));    }    catch (IOException e) {      log.info("Could not consume full client request", e);    }  }  protected HttpSolrCall getHttpSolrCall(HttpServletRequest request, HttpServletResponse response, boolean retry)  {    return new HttpSolrCall(this, this.cores, request, response, retry);  }  private boolean authenticateRequest(ServletRequest request, ServletResponse response, final AtomicReference<ServletRequest> wrappedRequest) throws IOException {    final AtomicBoolean isAuthenticated = new AtomicBoolean(false);    AuthenticationPlugin authenticationPlugin = this.cores.getAuthenticationPlugin();    if (authenticationPlugin == null) {      return true;    }    String header = ((HttpServletRequest)request).getHeader("SolrAuth");    if ((header != null) && (this.cores.getPkiAuthenticationPlugin() != null))      authenticationPlugin = this.cores.getPkiAuthenticationPlugin();    try {      log.debug("Request to authenticate: {}, domain: {}, port: {}", new Object[] { request, request.getLocalName(), Integer.valueOf(request.getLocalPort()) });      authenticationPlugin.doAuthenticate(request, response, new FilterChain() {        public void doFilter(ServletRequest req, ServletResponse rsp) throws IOException, ServletException {          isAuthenticated.set(true);          wrappedRequest.set(req);        } } );    }    catch (Exception e) {      e.printStackTrace();      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error during request authentication, ", e);    }    if (!isAuthenticated.get()) {      response.flushBuffer();      return false;    }    return true;  }  static enum Action  {    PASSTHROUGH, FORWARD, RETURN, RETRY, ADMIN, REMOTEQUERY, PROCESS;  }}

源码很多,我们拆分来看。

  1. 初始化solr core容器
this.cores = createCoreContainer(solrHome == null ? SolrResourceLoader.locateSolrHome() : Paths.get(solrHome, new String[0]), extraProperties);

我们看到SolrResourceLoader.locateSolrHome()
截取部分代码

 /* *可以找到源码中读取配置文件代码*   Context c = new InitialContext();*   home = (String)c.lookup("java:comp/env/solr/home");*   补充说明:*   env -entry元素声明Web应用的环境项。它由一个可选的description元素、一个*   env-entry-name元素(一个相对于java: comp/env环境JNDI名)、一个env-entry-value元素(项值)以及一个env-entry-type元素*  (java.lang程序包中一个类型的完全限定类名,java.lang.Boolean、java.lang.String等)组成。*   那么实际上就是去找web.xml下*   <!-- 配置solr home位置  -->    <env-entry>       <env-entry-name>solr/home</env-entry-name>       <env-entry-value>D:\solr\apache-tomcat-8.0.33\webapps\solr\solrhome</env-entry-value>       <env-entry-type>java.lang.String</env-entry-type>    </env-entry>*/ public static Path locateSolrHome()  {    String home = null;    try    {      Context c = new InitialContext();      home = (String)c.lookup("java:comp/env/solr/home");      log.info(new StringBuilder().append("Using JNDI solr.home: ").append(home).toString());    } catch (NoInitialContextException e) {      log.info("JNDI not configured for solr (NoInitialContextEx)");    } catch (NamingException e) {      log.info("No /solr/home in JNDI");    } catch (RuntimeException ex) {      log.warn(new StringBuilder().append("Odd RuntimeException while testing for JNDI: ").append(ex.getMessage()).toString());    }    if (home == null) {      String prop = "solr.solr.home";      home = System.getProperty(prop);      if (home != null) {        log.info(new StringBuilder().append("using system property ").append(prop).append(": ").append(home).toString());      }    }    if (home == null) {      home = "solr/";      log.info(new StringBuilder().append("solr home defaulted to '").append(home).append("' (could not find system property or JNDI)").toString());    }    return Paths.get(home, new String[0]);  }
/** * 创建solr core容器 * @param solrHome * @param extraProperties * @return */  protected CoreContainer createCoreContainer(Path solrHome, Properties extraProperties)  {    NodeConfig nodeConfig = loadNodeConfig(solrHome, extraProperties);    this.cores = new CoreContainer(nodeConfig, extraProperties, true);    this.cores.load();    return this.cores;  }/** * 获取配置文件  包含zookeeper的配置 * @param solrHome * @param nodeProperties * @return */  public static NodeConfig loadNodeConfig(Path solrHome, Properties nodeProperties)  {    SolrResourceLoader loader = new SolrResourceLoader(solrHome, null, nodeProperties);    if (!StringUtils.isEmpty(System.getProperty("solr.solrxml.location"))) {      log.warn("Solr property solr.solrxml.location is no longer supported. Will automatically load solr.xml from ZooKeeper if it exists");    }    String zkHost = System.getProperty("zkHost");    if (!StringUtils.isEmpty(zkHost)) {      try { SolrZkClient zkClient = new SolrZkClient(zkHost, 30000); Throwable localThrowable4 = null;        try { if (zkClient.exists("/solr.xml", true).booleanValue()) {            log.info("solr.xml found in ZooKeeper. Loading...");            byte[] data = zkClient.getData("/solr.xml", null, null, true);            return SolrXmlConfig.fromInputStream(loader, new ByteArrayInputStream(data));          }        }        catch (Throwable localThrowable2)        {          localThrowable4 = localThrowable2; throw localThrowable2;        }        finally        {          if (zkClient != null) if (localThrowable4 != null) try { zkClient.close(); } catch (Throwable localThrowable3) { localThrowable4.addSuppressed(localThrowable3); } else zkClient.close();          } } catch (Exception e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error occurred while loading solr.xml from zookeeper", e); }      log.info("Loading solr.xml from SolrHome (not found in ZooKeeper)");    }    return SolrXmlConfig.fromSolrHome(loader, loader.getInstancePath());  }

2、doFilter:全局性的过滤,将对特定的需要全文检索的请求进行一个过滤然后提供全文检索服务

HttpSolrCall call = getHttpSolrCall((HttpServletRequest)request, (HttpServletResponse)response, retry);

获取httpSolrCall实例

protected HttpSolrCall getHttpSolrCall(HttpServletRequest request, HttpServletResponse response, boolean retry)  {    return new HttpSolrCall(this, this.cores, request, response, retry);  }

调用call

 Action result = call.call();

下面我们重点看下HttpSolrCall 做了些什么,Solr 6.0 学习(九)会对HttpSolrCall做更详细的解答。

0 0
原创粉丝点击