深入解析 Karakeep(原 Hoarder)系统架构:Next.js 前端、SQLite 任务队列与三类 Worker 流水线

发布时间:2026/9/10 20:03:05
深入解析 Karakeep(原 Hoarder)系统架构:Next.js 前端、SQLite 任务队列与三类 Worker 流水线 深入解析 Karakeep原 Hoarder系统架构Next.js 前端、SQLite 任务队列与三类 Worker 流水线【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarderKarakeep项目前身名为 Hoarder是一个可自托管的收藏一切应用支持收藏链接、笔记与图片并提供基于 AI 的自动打标签和全文搜索。本文以 v0.28.0 时期的架构文档docs/versioned_docs/version-v0.28.0/07-development/04-architecture.md为骨架结合当前仓库源码逐层拆解其整体架构从 Next.js Web 应用、SQLite 数据存储与任务队列到 Crawling爬取、OpenAIAI 推理、Indexing全文索引三类核心 Worker并顺带梳理其随版本演进出的更多 Worker 类型。读完本文你将理解一条收藏链接从保存到可被全文搜索的完整调用链以及如何通过环境变量与 Docker Compose 部署这套架构。架构总览三个核心组件v0.28.0 的架构文档用极为精炼的方式给出了系统的全局视图整个系统由三个核心组件构成Web 应用Webapp基于 Next.js使用 SQLite 存储数据Worker 集群Workers从基于 SQLite 的任务队列中消费任务并执行辅助基础设施无头 Chrome 浏览器用于抓取页面、Meilisearch用于全文索引。其中 Worker 在 v0.28.0 文档中定义了三种任务类型任务类型职责关键依赖Crawling爬取使用运行在 Worker 容器内的无头 Chrome 浏览器抓取链接内容Headless ChromeOpenAIAI 推理调用 OpenAI API 对内容进行标签推断自动打标签OpenAI APIIndexing索引将内容索引到 Meilisearch以加速搜索时的检索Meilisearch这张图揭示了一个重要的设计取向用户请求处理Web 应用与重型后台处理爬取、AI 推理、索引完全解耦。用户在界面上保存一条链接后Web 应用只负责写入元数据并投递一个任务到队列真正的重活全部由 Worker 异步完成因此 Web 应用的响应延迟不会受爬取或 AI 调用耗时的影响。Web 应用层Next.js SQLite 的组合Web 应用位于 apps/web是一个基于 Next.js 的完整前端应用App Router 结构承载登录注册、仪表盘、书签管理、设置、阅读器等页面。其数据层使用 SQLite通过 packages/db/schema.ts 中基于 Drizzle ORM 定义的 schema 来建表和读写。从 schema.ts 可以窥见核心数据模型包括用户userusers表包含账号、角色admin/user、书签配额、存储配额以及自动打标签、自动摘要、标签风格如lowercase-hyphens、camelCase等 AI 相关设置书签bookmark链接、笔记、图片三类收藏的统一实体通过BookmarkTypes区分任务状态字段书签行上直接带有taggingStatus、summarizationStatus、embeddingStatus、crawlStatus等状态列这些状态正是 Worker 流水线各阶段的进度仪表盘。选择 SQLite 意味着单文件、零运维这也是该项目自托管友好定位的关键支撑——用户无需额外搭建 PostgreSQL只需挂载一个数据目录即可完成部署见下文 Docker Compose 部分。任务队列以 SQLite 为存储的轻量级队列架构文档强调Workers 消费来自基于 SQLite 的任务队列中的任务。在当前仓库中这套队列机制被抽象为一组与存储后端解耦的接口定义在 packages/shared/queueing.tsQueueT队列的抽象接口提供enqueue(payload, options)投递任务、stats()查询 pending/running/failed 等队列状态支持idempotencyKey幂等键、priority优先级、delayMs延迟执行、groupId等投递选项RunnerTWorker 端的运行器抽象run()启动消费循环通过pollIntervalMs轮询间隔、timeoutSecs任务超时、concurrency并发数等参数控制消费行为QueueRetryAfterError一种特殊错误抛出后任务会不消耗重试次数地延迟重试——源码注释明确说明这是为限流rate limiting场景设计的见 queueing.ts。队列的具体存储实现通过插件机制注册。默认的 SQLite 队列实现位于 packages/plugins/queue-liteque它通过 PluginManager 以PluginType.Queue类型自动注册为 Liteque 提供者见 queue-liteque/index.ts。这也意味着架构文档所描述的SQLite 队列是可以被替换的——仓库中还存在基于 Restate 的queue-restate插件供需要分布式队列能力的部署场景使用。Worker 集群三类核心任务的内部实现1. Crawling无头 Chrome 抓取链接内容爬取任务的入口是 apps/workers/workers/crawlerWorker.ts其核心执行函数runCrawlercrawlerWorker.ts#L331展示了完整的爬取流水线任务校验用zCrawlLinkRequestSchema校验任务载荷非法任务直接丢弃域名限流检查checkDomainRateLimit按目标域名维度做限流窗口期/最大请求数由CRAWLER_DOMAIN_RATE_LIMIT_WINDOW_MS等配置控制被限流时抛出QueueRetryAfterError并加入 40% 随机抖动jitter避免惊群效应见 crawlerWorker.ts#L209-L257内容类型探测通过getContentTypeAndMetadata预探测 URL 的内容类型——若为 PDF 或图片则把链接书签就地转换为对应的资产书签handleAsAssetBookmark否则走 HTML 网页路径浏览器抓取与解析调用 crawler/crawlAndParse.ts 中的crawlAndParseUrl完成页面抓取、解析与资产持久化。浏览器生命周期管理连接无头 Chrome、初始化 adblocker 等封装在 crawler/browser.ts归档收尾抓取成功后执行截图、PDF、完整页面归档等可选归档逻辑archivalLogic投递后续任务enqueuePostCrawlJobscrawlerWorker.ts#L264-L329把本次抓取的成果转交给流水线的下一环——按配置投递 Embeddings/OpenAI 任务打标签、摘要、搜索重建索引任务以及视频下载与 webhook 触发任务。值得注意的是Worker 运行器在onComplete中会把书签链接的crawlStatus置为success在重试耗尽numRetriesLeft 0的onError中则会把crawlStatus置为failure并在同一个事务里清理taggingStatus、summarizationStatus、embeddingStatus等遗留的 pending 状态见 crawlerWorker.ts#L125-L192保证失败任务不会在后续环节悬空。2. OpenAIAI 自动打标签与内容推断在 v0.28.0 的架构里OpenAI 任务负责推断内容标签。演进到当前仓库后这部分已扩展为 apps/workers/workers/inference 目录下的inferenceWorker.ts、tagging.ts与summarize.ts三个文件即除了自动打标签还加入了自动摘要能力。用户侧相关的开关autoTaggingEnabled、autoSummarizationEnabled、标签风格tagStyle、标签语言inferredTagLang等都定义在用户表的 schema 中见 packages/db/schema.ts#L100-L117。从enqueuePostCrawlJobs的源码可以看出推理任务的触发策略默认情况下若开启了自动向量索引embedding.enableAutoIndexing抓取完成后会先投递 Embeddings 任务并在向量化完成后触发打标签runTaggingOnComplete: true否则直接投递 OpenAI 打标签任务并总是投递一个摘要任务见 crawlerWorker.ts#L277-L303。3. Indexing向 Meilisearch 建立全文索引索引任务由 apps/workers/workers/searchWorker.ts 实现负责把书签内容写入 Meilisearch 以支持高速全文搜索。搜索引擎同样通过插件机制接入packages/plugins/search-meilisearch 在检测到 Meilisearch 配置isConfigured()后以PluginType.Search注册为 MeiliSearch 提供者见 search-meilisearch/index.ts。搜索索引的触发方式通过 packages/shared/search.ts 中的triggerSearchReindex统一封装爬取完成后即调用它入队重建索引任务见 crawlerWorker.ts#L306。从源码结构看架构的演进远不止三类任务v0.28.0 文档仅列了三种任务类型但当前仓库的 Worker 入口 apps/workers/index.ts 已显式定义了 13 种 Worker 构建器workerBuilders可以推断架构随版本演进大幅扩展核心三件套crawler爬取、inferenceAI 推理、search搜索索引围绕爬取的配套lowPriorityCrawler低优先级爬取队列、embeddings向量化、video视频下载、assetPreprocessing资产预处理增值能力feedRSS 订阅刷新、ruleEngine规则引擎、webhookWebhook 触发、backup自动备份、adminMaintenance后台维护如整理资产、迁移链接 HTML 内容、以及轮询式启动的import批量导入。每种 Worker 的启用与禁用均可通过WORKERS_ENABLED/WORKERS_DISABLED环境变量精确控制见 apps/workers/index.ts#L114-L125这为自托管用户在资源受限环境下的裁剪部署提供了极大灵活性——例如纯笔记用户完全可以禁用爬取与 AI 相关的 Worker。部署拓扑一条 Docker Compose 背后的三个容器架构文档描述的组件在实际部署中被编排进 docker/docker-compose.yml共三个服务webghcr.io/karakeep-app/karakeep镜像映射宿主机3000端口挂载data:/data数据卷即 SQLite 数据文件所在目录并通过环境变量指向另外两个服务MEILI_ADDR: http://meilisearch:7700与BROWSER_WEB_URL: http://chrome:9222chromeghcr.io/karakeep-app/karakeep-chrome镜像即架构图与文档中提到的运行在 Worker 容器内的无头 Chrome 浏览器。它通过--disable-gpu、--disable-dev-shm-usage、--disable-blink-featuresAutomationControlled、--window-size1440,900等参数以无头模式启动并暴露调试端口 9222 供 Worker 连接docker-compose.yml#L22-L31meilisearchgetmeili/meilisearch:v1.41.0镜像挂载meilisearch:/meili_data卷并设置MEILI_NO_ANALYTICS: true关闭遥测docker-compose.yml#L32-L40。这套拓扑正是架构文档Web 应用 Worker 辅助服务抽象的具体落地Web 与 Worker 打包在同一个镜像里通过环境变量决定各进程的行为Chrome 与 Meilisearch 作为独立容器提供能力。关键环境变量从配置源码看可调参数Worker 侧的运行参数集中在 packages/shared/config.ts 的crawler配置块config.ts#L398-L437中常用项包括环境变量作用默认说明CRAWLER_NUM_WORKERS爬取 Worker 并发数影响爬取吞吐对应 Runner 的concurrencyCRAWLER_JOB_TIMEOUT_SEC单个爬取任务的超时时间对应 Runner 的timeoutSecsBROWSER_WEB_URL/BROWSER_WEBSOCKET_URL无头 Chrome 的连接地址HTTP 调试端口或 WebSocketCompose 中指向http://chrome:9222BROWSER_CONNECT_ONDEMAND是否按需建立浏览器连接影响浏览器资源占用模式CRAWLER_DOMAIN_RATE_LIMIT_WINDOW_MS/CRAWLER_DOMAIN_RATE_LIMIT_MAX_REQUESTS按域名限流的窗口与请求上限同时设置才启用配合QueueRetryAfterError使用CRAWLER_STORE_SCREENSHOT/CRAWLER_FULL_PAGE_SCREENSHOT/CRAWLER_STORE_PDF/CRAWLER_FULL_PAGE_ARCHIVE控制截图、PDF、整页归档等归档行为归档功能开关CRAWLER_VIDEO_DOWNLOAD及CRAWLER_VIDEO_DOWNLOAD_MAX_SIZE是否下载视频及大小上限控制视频类书签的处理CRAWLER_ENABLE_ADBLOCKER/CRAWLER_ENABLE_AUTOCONSENT是否启用广告拦截与 Cookie 同意自动处理影响页面抓取质量与合规完整的环境变量清单见 环境变量配置文档。一次收藏的完整旅程端到端链路回顾综合以上分析一条链接被保存后经历的整体链路可以概括为用户在 Web 应用保存链接书签行落库到 SQLite同时向任务队列投递 Crawling 任务Crawling Worker 消费任务先做域名限流检查与内容类型探测再驱动无头 Chrome 抓取页面、解析正文并归档截图/PDF/整页快照等资产抓取成功后enqueuePostCrawlJobs依次投递 Embeddings/OpenAI 任务自动打标签、摘要与搜索重建索引任务OpenAI/Embeddings Worker 对内容做 AI 推理写回标签与摘要Search Worker 把内容写入 Meilisearch用户在搜索框中输入关键词Web 应用通过 Meilisearch 快速返回全文检索结果。整条链路中SQLite 既是业务数据的主存储也是任务队列的存储介质无头 Chrome 承担重的页面渲染抓取Meilisearch 承担快的全文检索OpenAI 类服务承担智能的内容理解。三者通过队列解耦、通过状态列追踪进度构成了一个结构清晰、各司其职、易于自托管的可插拔架构。小结本文从 v0.28.0 架构文档的三句话出发结合当前仓库源码还原了 Karakeep 的完整架构面貌Next.js SQLite 的 Web 应用层、可插拔的队列抽象默认 SQLite 队列、三类核心 Worker 的流水线实现以及它们在 Docker Compose 中的部署拓扑。理解这套架构无论是排查爬取失败、调优 Worker 并发还是为资源受限环境裁剪 Worker 类型都能有的放矢。更进一步仓库文档目录还提供了 开发环境搭建 与 数据库说明 等资料可作为继续深入该架构的下一站。【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考