Apache StormCrawler 3.6.0 常用工具与 API 详解
Apache StormCrawler 3.6.0 常用工具与 API 详解:以 core 模块为纲梳理种子注入、协议、抓取、解析、索引、状态六层组件 API 与 MemorySpout、StdOutIndexer 等本地调试组件,给出 Flux 拓扑流定义、常用 CLI 命令与最小可运行示例,全部经 Ubuntu 22.04 实测验证。
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.JSoupParserBolt | sitemap/feed/HTML 解析与外链提取 |
| 解析(tika 模块) | tika.ParserBolt、tika.RedirectionBolt | 非 HTML 文档解析与重定向分流 |
| 索引 | indexing.StdOutIndexer、AbstractIndexerBolt | 把解析结果转换为索引文档 |
| 状态 | persistence.MemoryStatusUpdater、persistence.StdOutStatusUpdater、persistence.DefaultScheduler、persistence.urlbuffer.SimpleURLBuffer | URL 状态存储与调度 |
💡 说明: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 解析组件
| 组件 | 类名 | 职责 |
|---|---|---|
| sitemap | org.apache.stormcrawler.bolt.SiteMapParserBolt | 识别并解析 sitemap.xml,配合 sitemap.discovery 自动发现 |
| feed | org.apache.stormcrawler.bolt.FeedParserBolt | 识别 RSS/Atom feed,抓取条目并标记 isFeed 元数据 |
| HTML 解析 | org.apache.stormcrawler.bolt.JSoupParserBolt | jsoup 抽取正文/标题/外链,textextractor.* 控制抽取范围 |
| 分流 | org.apache.stormcrawler.tika.RedirectionBolt | HTML/非 HTML 内容分流(tika 流或默认流) |
| Tika 解析 | org.apache.stormcrawler.tika.ParserBolt | PDF/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.StatusUpdaterBolt | URLFrontier 后端(默认拓扑) |
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.DefaultScheduler | scheduler.class | 按 fetchInterval.* 计算重抓时间 |
| org.apache.stormcrawler.persistence.urlbuffer.SimpleURLBuffer | urlbuffer.class | spout 端按站点轮转的 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 | 构建 uberjar | BUILD 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 后进程干净退出。
八、注意事项
⚠️ 注意:
- MemorySpout/MemoryStatusUpdater/StdOutStatusUpdater/StdOutIndexer 属于调试级组件:状态在 JVM 内存、索引输出到 stdout,不适用于生产与多 worker 场景。
- MemorySpout 的 varargs 构造器在 Flux 中必须用嵌套列表传参。
- 所有 URL(含解析出的外链)都要通过 urlfilters.json 过滤链;archetype 默认拒绝 localhost/私网地址(防 SSRF),抓内网站点需按需注释。
- 继承 AbstractIndexerBolt/AbstractStatusUpdaterBolt 自定义组件时,记得同步在拓扑 YAML 中替换组件类名并保持 status 流接线完整。
- 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