Solr 6.0 學習(八) SolrDispatchFilter源碼解析及solr擴展

傳送門:老版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做更詳細的解答。

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