Apache StormCrawler 3.6.0 常用工具与 API 详解

Apache StormCrawler 3.6.0 常用工具与 API 详解:以 core 模块为纲梳理种子注入、协议、抓取、解析、索引、状态六层组件 API 与 MemorySpout、StdOutIndexer 等本地调试组件,给出 Flux 拓扑流定义、常用 CLI 命令与最小可运行示例,全部经 Ubuntu 22.04 实测验证。

Apache StormCrawlerApache StormCrawler2026/10/12

Apache StormCrawler 3.6.0 常用工具与 API 详解

目标:梳理 Apache StormCrawler 3.6.0 的常用组件 API 与配套工具。StormCrawler 以 Apache Storm 的 spout/bolt 组件为骨架,通过 Flux(YAML)装配拓扑、通过 JSON 配置装配过滤器链。本文按「种子注入 → 协议 → 抓取 → 解析 → 索引 → 状态」六层梳理 core 模块的常用 API,并给出 Flux 流定义、CLI 工具与最小可运行示例。组件类名与行为均在 Ubuntu 22.04.5 + StormCrawler 3.6.0 + Storm 2.8.8 环境实测核实。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS
  • Java:OpenJDK 17.0.20.1
  • StormCrawler 版本:3.6.0(core / tika / urlfrontier 模块,官方 archetype 生成工程)
  • Storm 运行时:Apache Storm 2.8.8(/opt/apache-storm-2.8.8/)
  • 示例工程目录:/root/build/crawler-demo/
  • 演示账号:root

二、组件体系总览

StormCrawler 3.6.0 的 core 模块(stormcrawler-core-3.6.0.jar,实测 jar 内类清单)按职责分层:

层常用类(org.apache.stormcrawler.*)职责
种子注入spout.MemorySpout、spout.FileSpout向拓扑注入待抓 URL
协议protocol.okhttp.HttpProtocol、protocol.httpclient.HttpProtocol发送 HTTP/HTTPS 请求(Protocol 接口实现)
抓取bolt.URLPartitionerBolt、bolt.FetcherBolt按站点分区并礼貌抓取
解析bolt.SiteMapParserBolt、bolt.FeedParserBolt、bolt.JSoupParserBoltsitemap/feed/HTML 解析与外链提取
解析(tika 模块)tika.ParserBolt、tika.RedirectionBolt非 HTML 文档解析与重定向分流
索引indexing.StdOutIndexer、AbstractIndexerBolt把解析结果转换为索引文档
状态persistence.MemoryStatusUpdater、persistence.StdOutStatusUpdater、persistence.DefaultScheduler、persistence.urlbuffer.SimpleURLBufferURL 状态存储与调度

💡 说明:StormCrawler 的「API 体系」= Storm 组件基类(继承 BaseRichSpout/BaseRichBolt)+ 配置驱动(crawler-default.yaml → crawler-conf.yaml)+ Flux 装配(YAML)。绝大多数功能通过组合现成组件并调整配置即可完成,无需编写 Java 代码。

三、前置条件

已按《Ubuntu 22.04 安装 Apache StormCrawler 3.6.0 指南》与《构建并运行示例抓取应用指南》完成工程构建,并可用本地模式运行最小拓扑。核实 core 模块包含上述类的命令:

unzip -l ~/.m2/repository/org/apache/stormcrawler/stormcrawler-core/3.6.0/stormcrawler-core-3.6.0.jar | grep -E "spout/|persistence/|indexing/StdOutIndexer|okhttp/HttpProtocol.class" | awk "{print \$4}"

实测回显(节选):

org/apache/stormcrawler/persistence/MemoryStatusUpdater.class
org/apache/stormcrawler/persistence/StdOutStatusUpdater.class
org/apache/stormcrawler/spout/FileSpout.class
org/apache/stormcrawler/spout/MemorySpout.class
org/apache/stormcrawler/indexing/StdOutIndexer.class
org/apache/stormcrawler/protocol/okhttp/HttpProtocol.class

四、核心组件 API 详解

4.1 种子注入 Spout

(1)MemorySpout(内存队列,本地调试)

构造签名(源码实测):MemorySpout(String... urls)、MemorySpout(boolean withDiscoveredStatus, String... urls)。内部维护静态内存队列:MemoryStatusUpdater 把调度结果通过静态方法 MemorySpout.add(url, metadata, nextFetch) 回写,spout 按到期时间逐条发出。

Flux 中的用法(varargs 需用嵌套列表传参,实测可用):

spouts:
  - id: "spout"
    className: "org.apache.stormcrawler.spout.MemorySpout"
    constructorArgs:
      - - "http://localhost:8080/index.html"
    parallelism: 1

⚠️ 注意:仅适用于本地/单 worker 模式;若写成单值 - "http://..."(非嵌套列表),启动报 Couldn't find a suitable constructor for class 'org.apache.stormcrawler.spout.MemorySpout'。

(2)FileSpout(文件种子)

从目录中按文件名过滤读取种子文件,每行一条 URL(可用制表符附加 JSON 元数据)。Flux 用法(非 varargs 构造器,实测签名 FileSpout(String dir, String filter)):

spouts:
  - id: "spout"
    className: "org.apache.stormcrawler.spout.FileSpout"
    constructorArgs:
      - "/root/build/crawler-demo/seeds"
      - "seeds.txt"
    parallelism: 1

(3)URLFrontier Spout(生产队列后端)

archetype 默认拓扑使用 org.apache.stormcrawler.urlfrontier.Spout(stormcrawler-urlfrontier 模块),从 URLFrontier gRPC 服务分桶拉取到期 URL;对应状态组件为 org.apache.stormcrawler.urlfrontier.StatusUpdaterBolt。生产部署见《配置参数详解》5.8 节。

4.2 协议层 Protocol

协议实现类通过配置指定(crawler-conf.yaml 实测值):

  http.protocol.implementation: "org.apache.stormcrawler.protocol.okhttp.HttpProtocol"
  https.protocol.implementation: "org.apache.stormcrawler.protocol.okhttp.HttpProtocol"
  • okhttp 版 org.apache.stormcrawler.protocol.okhttp.HttpProtocol:默认推荐,支持超时、代理、重试等全部 http.* 配置项;
  • httpclient 版 org.apache.stormcrawler.protocol.httpclient.HttpProtocol:内置默认值(crawler-default.yaml 中为默认实现),archetype 工程已切换为 okhttp;
  • 自定义协议:实现 org.apache.stormcrawler.protocol.Protocol 接口(getResponse(URL, Metadata)),在配置中替换实现类名即可。

4.3 抓取组件

(1)URLPartitionerBolt(分区):按 partition.url.mode(默认 byHost)计算 key 字段,同站点 URL 进入同一 fetcher 队列,实现礼貌抓取。实测流定义:

  - from: "partitioner"
    to: "fetcher"
    grouping:
      type: FIELDS
      args: ["key"]

(2)FetcherBolt(抓取执行):多线程抓取 + robots 合规 + 队列节流(fetcher.server.delay / fetcher.max.crawl.delay)。关键配置:fetcher.threads.number(archetype 默认 50)、fetcher.threads.per.queue(默认 1)。抓取结果经 status 流上报 FETCHED/ERROR,页面内容发往解析链。实测日志:

13:33:00.256 [FetcherThread #40] INFO  o.a.s.b.FetcherBolt - [Fetcher #4] Fetched http://localhost:8080/index.html with status 200 in msec 4

4.4 解析组件

组件类名职责
sitemaporg.apache.stormcrawler.bolt.SiteMapParserBolt识别并解析 sitemap.xml,配合 sitemap.discovery 自动发现
feedorg.apache.stormcrawler.bolt.FeedParserBolt识别 RSS/Atom feed,抓取条目并标记 isFeed 元数据
HTML 解析org.apache.stormcrawler.bolt.JSoupParserBoltjsoup 抽取正文/标题/外链,textextractor.* 控制抽取范围
分流org.apache.stormcrawler.tika.RedirectionBoltHTML/非 HTML 内容分流(tika 流或默认流)
Tika 解析org.apache.stormcrawler.tika.ParserBoltPDF/Office 等二进制文档解析(需 parse.tika.config.xml)

JSoupParserBolt 实测日志(解析耗时与外链输出):

13:33:00.266 [Thread-39-parse-executor[6, 6]] INFO  o.a.s.b.JSoupParserBolt - Parsing : starting http://localhost:8080/index.html
13:33:00.437 [Thread-39-parse-executor[6, 6]] INFO  o.a.s.b.JSoupParserBolt - Parsed http://localhost:8080/index.html in 106 msec

解析行为由 JSON 过滤链装配(archetype 自带):

  • urlfilters.json:URL 过滤链(BasicURLNormalizer、RegexURLFilter 等),所有进入状态库的 URL 都会经过;
  • parsefilters.json:解析过滤器链(如 CanonicalLinker、SubDocumentFilter 等);
  • jsoupfilters.json:jsoup DOM 级过滤器。

自定义过滤器的扩展点:实现 URLFilter / ParseFilter / JSoupFilter 接口,在对应 JSON 中声明实现类即可。

4.5 索引组件

StdOutIndexer(本地调试):org.apache.stormcrawler.indexing.StdOutIndexer,按 indexer.md.mapping 组装文档后逐字段打印到标准输出(字段名 + 制表符 + 值)。实测输出:

content	StormCrawler Local Test Site Welcome to the local crawler test site. Page One Page Two Page Three
url	http://localhost:8080/index.html
domain	localhost
format	html
title	Local Test Site

启动时先打印字段映射日志(实测):

13:32:59.311 [Thread-45-index-executor[5, 5]] INFO  o.a.s.i.StdOutIndexer - Mapping key parse.title to field title

AbstractIndexerBolt(自定义索引基类):接入 OpenSearch/Solr/SQL 等后端时继承该基类,实现 toDocument(String url, Content content, Metadata metadata) 返回后端文档对象即可;字段映射逻辑(indexer.* 配置)已由基类处理。

4.6 状态组件

组件类名适用场景
内存状态org.apache.stormcrawler.persistence.MemoryStatusUpdater本地调试,与 MemorySpout 静态队列协作
标准输出状态org.apache.stormcrawler.persistence.StdOutStatusUpdater打印状态变化用于排查
生产状态org.apache.stormcrawler.urlfrontier.StatusUpdaterBoltURLFrontier 后端(默认拓扑)

AbstractStatusUpdaterBolt 是所有状态组件的基类:接收 status 流元组 → 按 status.updater.use.cache 对 DISCOVERED URL 去重(Guava 缓存,规格 status.updater.cache.spec)→ 经 scheduler.class(默认 DefaultScheduler)计算 nextFetch → 交给后端存储。实测源码关键逻辑:状态组件把调度结果回写到 MemorySpout 静态队列(MemorySpout.add(url, metadata, nextFetch)),注释明确要求与 MemorySpout 同 JVM 搭配使用。

4.7 调度与缓冲

类配置项说明
org.apache.stormcrawler.persistence.DefaultSchedulerscheduler.class按 fetchInterval.* 计算重抓时间
org.apache.stormcrawler.persistence.urlbuffer.SimpleURLBufferurlbuffer.classspout 端按站点轮转的 URL 缓冲

五、Flux 拓扑流定义

Flux 以 YAML 声明组件与流。最小抓取拓扑的数据流(与《构建并运行示例抓取应用指南》实测拓扑一致):

spout --SHUFFLE--> partitioner --FIELDS(key)--> fetcher --LOCAL_OR_SHUFFLE--> sitemap
      --LOCAL_OR_SHUFFLE--> feed --LOCAL_OR_SHUFFLE--> parse --LOCAL_OR_SHUFFLE--> shunt
      --LOCAL_OR_SHUFFLE--> tika --LOCAL_OR_SHUFFLE--> index
各组件 --FIELDS(url)--> status(streamId: status)
  • 默认流承载页面内容(StringTabScheme 的 url+metadata 元组);
  • status 流承载 URL 状态上报(FETCHED/DISCOVERED/ERROR),各组件统一发往 status 组件;
  • grouping 常用类型:SHUFFLE(随机)、FIELDS(按字段哈希,fetcher 按 key、status 按 url)、LOCAL_OR_SHUFFLE(优先本 worker)。

六、常用 CLI 工具

命令用途实测示例
mvn archetype:generate生成 crawler 工程mvn archetype:generate -B -DarchetypeGroupId=org.apache.stormcrawler -DarchetypeArtifactId=stormcrawler-archetype -DarchetypeVersion=3.6.0 ...(archetype 退出码 0)
mvn clean package构建 uberjarBUILD SUCCESS,产物 106MB
storm version查看 Storm 版本输出 Storm 2.8.8
storm local本地模式运行拓扑STORM_JAR_JVM_OPTS="-Xms256m -Xmx512m" /opt/apache-storm-2.8.8/bin/storm local target/crawler-demo-1.0-SNAPSHOT.jar org.apache.storm.flux.Flux crawler-local.flux --local-ttl 150
storm jar提交分布式拓扑storm jar target/crawler-demo-1.0-SNAPSHOT.jar org.apache.storm.flux.Flux crawler.flux -c nimbus.host=...
jps -v核实 JVM 参数实测回显含 -Xms256m -Xmx512m -Dstorm.local.sleeptime=150
wget / curl验证目标站点与结果wget -qO- http://127.0.0.1:8080/index.html
unzip -l / python3查看 jar 内类与提取配置见第 3.2 节命令与回显

URLFrontier 后端的种子注入(生产路径,工程内自带客户端):构建后用 uberjar 内的 crawlercommons.urlfrontier.client.Client 执行 PutURLs -f seeds.txt 向服务写入种子 URL。

七、完整最小调用示例

一个零 Java 代码的最小抓取应用只需三步(详细步骤见《构建并运行示例抓取应用指南》):

# 1. 构建工程
cd /root/build/crawler-demo && mvn -B clean package

# 2. 准备 crawler-local.flux(MemorySpout + MemoryStatusUpdater + StdOutIndexer)与本地站点
# 3. 本地模式运行(150 秒后自动退出)
STORM_JAR_JVM_OPTS="-Xms256m -Xmx512m" /opt/apache-storm-2.8.8/bin/storm local \
  target/crawler-demo-1.0-SNAPSHOT.jar org.apache.storm.flux.Flux crawler-local.flux --local-ttl 150

实测结果:4 个页面全部抓取(Fetched ... with status 200),StdOutIndexer 输出 4 篇索引文档(url/content/title/domain/format 字段),Done shutting down in process zookeeper 后进程干净退出。

八、注意事项

⚠️ 注意:

  1. MemorySpout/MemoryStatusUpdater/StdOutStatusUpdater/StdOutIndexer 属于调试级组件:状态在 JVM 内存、索引输出到 stdout,不适用于生产与多 worker 场景。
  2. MemorySpout 的 varargs 构造器在 Flux 中必须用嵌套列表传参。
  3. 所有 URL(含解析出的外链)都要通过 urlfilters.json 过滤链;archetype 默认拒绝 localhost/私网地址(防 SSRF),抓内网站点需按需注释。
  4. 继承 AbstractIndexerBolt/AbstractStatusUpdaterBolt 自定义组件时,记得同步在拓扑 YAML 中替换组件类名并保持 status 流接线完整。
  5. worker 进程建议用 topology.worker.childopts 或 STORM_JAR_JVM_OPTS 限制堆(256-512MB 起步),并用 jps -v 验证。

九、常见问题

Q1:如何把抓取结果写入真正的搜索引擎? 替换 index 组件:OpenSearch 用 stormcrawler-opensearch 模块的 IndexerBolt,Solr 用 stormcrawler-solr 模块,或继承 AbstractIndexerBolt 自行实现 toDocument;流接线与 indexer.* 配置不变。

Q2:如何控制外链抓取深度或频率? 深度用 metadata.track.depth 配合 URLFilter(如 MaxDepthFilter);频率用 fetchInterval.* 与 scheduler.class(默认 DefaultScheduler,可换 AdaptiveScheduler)。

Q3:组件类名找不到(ClassNotFoundException)? 确认模块已加入 pom 依赖(如 tika 组件需 stormcrawler-tika、urlfrontier 组件需 stormcrawler-urlfrontier)并重新 mvn package。

Q4:status 流报「序列化异常」? Flux 模式必须注册 Kryo:topology.kryo.register 中包含 org.apache.stormcrawler.Metadata 与 org.apache.stormcrawler.persistence.Status(archetype 生成文件已含,自建拓扑易遗漏)。

Q5:本地模式如何看实时指标? LoggingMetricsConsumer 每 10 秒打印指标(实测样例 4:fetcher fetcher_average_persec {bytes_fetched_perSec=91.6..., fetched_perSec=0.40...});分布式模式可换 StormCrawler 的 Prometheus/CloudWatch 指标模块。

十、参考链接

  • StormCrawler 官方文档:https://stormcrawler.net/
  • 源码仓库:https://github.com/apache/stormcrawler
  • Maven Central(org.apache.stormcrawler):https://repo1.maven.org/maven2/org/apache/stormcrawler/
  • Apache Storm Flux 文档:https://storm.apache.org/releases/2.8.8/flux.html
  • Apache Storm 本地模式文档:https://storm.apache.org/releases/2.8.8/LocalCluster.html