SOLR深度源码系列解读专栏(二):SOLR 的架构总览

2.1 前言

在上一篇文章中,我们已经完成了 SOLR 的源码环境搭建,成功运行了一个简单的实例,并初步浏览了源码目录结构。现在,我们将目光转向 SOLR 的整体架构,探索它如何将复杂的功能组织成一个高效的搜索系统。通过本篇,你将了解 SOLR 的核心组件是如何协作的,请求是如何从客户端到达服务器并返回结果的,以及源码中哪些关键类扮演了重要角色。这不仅是后续深入分析的基础,也是理解 SOLR 设计思想的起点。

SOLR 的架构设计兼顾了性能、扩展性和易用性。无论是单机部署还是分布式环境(SolrCloud),其核心思想都围绕着“高效索引”和“快速查询”展开。本文将从高层视图逐步深入到源码细节,带你一窥 SOLR 的全貌。


2.2 SOLR 的整体架构

2.2.1 高层视图

SOLR 的架构可以分为三个主要层次:

  1. 客户端层:通过 HTTP 请求(通常是 RESTful API)与 SOLR 交互,支持多种语言(如 Java 的 SolrJ、Python 的 pysolr 等)。
  2. 服务端层:SOLR 的核心运行时,包括嵌入式 Jetty 服务器、请求分发逻辑和核心组件。
  3. 存储层:基于 Lucene 的索引文件系统,负责持久化数据。

从功能上看,SOLR 的架构可以用下图简要表示(文字描述替代图形):

客户端请求(HTTP) → SolrDispatchFilter(请求入口) → CoreContainer(核心容器) → SolrCore(具体核心) → Lucene(索引与搜索)

2.2.2 核心概念解析

在深入源码之前,我们需要理解 SOLR 中的几个关键概念,它们贯穿整个架构:

  • Core:一个独立的核心,包含自己的索引、配置(solrconfig.xmlschema.xml)和数据。通常用于单机模式。
  • Collection:分布式环境下的逻辑概念,一个 Collection 可以分布在多个节点上,包含多个 Shard。
  • Shard:Collection 的分片,每个 Shard 是一个独立的索引单元。
  • Replica:Shard 的副本,用于高可用性和负载均衡。
  • SolrCloud:SOLR 的分布式模式,通过 ZooKeeper 管理集群状态。

这些概念在源码中以类和数据结构的形式体现,后文会逐一分析。

2.2.3 单机 vs 分布式

  • 单机模式:一个 SOLR 实例运行多个 Core,所有数据存储在本地。
  • 分布式模式(SolrCloud):多个 SOLR 节点组成集群,通过 ZooKeeper 协调分片和副本,数据分布存储。

本篇将以单机模式为主,逐步引入分布式概念,为后续专门的 SolrCloud 篇章做铺垫。


2.3 请求处理流程:从 HTTP 到响应

SOLR 的核心工作是处理客户端请求。无论是索引文档还是查询数据,SOLR 都通过 HTTP 接口接收请求,经过一系列组件处理后返回结果。以下是请求处理的高层流程:

  1. 客户端发送请求:如 http://localhost:8983/solr/mycore/update
  2. 请求抵达服务器:Jetty 接收并交给 SOLR 的过滤器。
  3. 请求分发:根据路径(如 /update/select)路由到对应处理器。
  4. 核心处理:调用 SolrCore 执行索引或查询操作。
  5. 结果返回:封装响应(如 JSON)返回客户端。

接下来,我们通过源码逐步剖析这一流程。


2.4 源码入口:SolrDispatchFilter

SolrDispatchFilter 是 SOLR 处理 HTTP 请求的起点,位于 solr/server/solr-webapp/webapp/WEB-INF/web.xml 中定义,作为 Servlet 过滤器拦截所有请求。

2.4.1 初始化

SolrDispatchFilter 的初始化发生在 SOLR 启动时:

1
2
3
4
5
6
public void init(FilterConfig config) throws ServletException {
  this.config = config;
  this.pathPrefix = config.getInitParameter("path-prefix");
  coreContainer = createCoreContainer(getSolrHome(), getSolrConfig());
  log.info("SolrDispatchFilter.init() done");
}
  • coreContainer:全局的 CoreContainer 实例,管理所有 Core。
  • getSolrHome():读取 SOLR 的数据目录(默认 solr/)。

2.4.2 请求处理

核心方法是 doFilter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws IOException, ServletException {
  HttpSolrCall call = null;
  try {
    call = new HttpSolrCall(this, coreContainer, (HttpServletRequest)request, 
                            (HttpServletResponse)response, false);
    call.init();
    call.call();
  } finally {
    if (call != null) call.destroy();
  }
}
  • HttpSolrCall:封装单个请求的处理逻辑。
  • init():解析请求路径和参数。
  • call():执行具体操作。

2.4.3 HttpSolrCall 的作用

HttpSolrCall 是请求的分发中枢。它根据 URL 路径(如 /mycore/select)决定调用哪个核心和处理器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
protected void call() throws IOException {
  SolrCore core = coreContainer.getCore(coreName);
  if (core == null) {
    sendError(404, "No such core: " + coreName);
    return;
  }
  try {
    SolrRequestHandler handler = core.getRequestHandler(path);
    if (handler != null) {
      core.execute(handler, req, rsp);
    } else {
      sendError(404, "No handler for: " + path);
    }
  } finally {
    core.close();
  }
}
  • coreName:从 URL 中提取的核心名称(如 mycore)。
  • path:请求路径(如 /select)。
  • SolrRequestHandler:具体的处理器(如查询或更新)。

2.5 CoreContainer:核心管理

CoreContainer 是 SOLR 的“大脑”,负责加载和管理所有 Core。

2.5.1 初始化

1
2
3
4
5
6
public CoreContainer(String solrHome, File solrXml) {
  this.solrHome = solrHome;
  this.loadSolrXML(solrXml);
  this.cores = new CoreDescriptors(this);
  this.startBackgroundTasks();
}
  • solr.xml:定义 Core 的配置文件。
  • cores:存储所有 SolrCore 实例。

2.5.2 获取 Core

1
2
3
4
5
public SolrCore getCore(String name) {
  SolrCore core = cores.getCoreFromAnyList(name, false);
  if (core != null) core.open();
  return core;
}
  • getCoreFromAnyList:从内存中查找 Core。

2.5.3 生命周期管理

CoreContainer 还负责 Core 的创建、销毁和重载,确保系统资源的高效利用。


2.6 SolrCore:核心执行单元

SolrCore 是 SOLR 的执行单元,每个 Core 独立管理自己的索引和配置。

2.6.1 结构

1
2
3
4
5
6
7
8
9
public class SolrCore implements Closeable {
  private final String name;
  private final IndexReaderFactory indexReaderFactory;
  private final SolrConfig solrConfig;
  private final SchemaFactory schemaFactory;
  private final UpdateHandler updateHandler;
  private final Searcher searcher;
  // ... 其他成员
}
  • solrConfig:配置文件 solrconfig.xml 的内存表示。
  • updateHandler:处理索引更新。
  • searcher:执行查询。

2.6.2 执行请求

execute 方法是核心逻辑:

1
2
3
public void execute(SolrRequestHandler handler, SolrQueryRequest req, SolrQueryResponse rsp) {
  handler.handleRequest(req, rsp);
}
  • SolrQueryRequest:封装请求参数。
  • SolrQueryResponse:封装响应数据。

2.7 请求处理示例:查询流程

以一个查询请求为例(http://localhost:8983/solr/mycore/select?q=title:Hello):

  1. 客户端发送请求:HTTP GET 请求到达 Jetty。
  2. SolrDispatchFilter 拦截:创建 HttpSolrCall,解析 URL。
  3. 路由到 CoreCoreContainer 返回 mycoreSolrCore
  4. 调用 SearchHandler
    1
    2
    3
    4
    5
    6
    7
    8
    
    public class SearchHandler extends RequestHandlerBase {
      public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) {
        SolrParams params = req.getParams();
        String q = params.get("q");
        QueryResponseWriter writer = getResponseWriter(req);
        rsp.addResponse(writer.write(search(q, req)));
      }
    }
    
  5. 返回结果:JSON 格式的查询结果。

2.8 分布式架构简介

在 SolrCloud 中,架构略有不同:

  • ZooKeeper:存储集群状态(clusterstate.json)。
  • ShardHandler:分发请求到多个节点。
  • CloudSolrClient:客户端与集群交互。

详细分析将在后续篇章展开。


2.9 总结

本篇从高层视图到源码细节,全面剖析了 SOLR 的架构。通过 SolrDispatchFilterCoreContainerSolrCore 的分析,我们理解了请求处理的核心流程。下一篇文章将深入探讨 索引构建与更新机制,带你走进 SOLR 的数据管理世界。

updatedupdated2025-03-312025-03-31