ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Karakeep(原 Hoarder)系统架构深度解析:Next.js + SQLite 与 Worker 任务队列驱动的书签处理流水线

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 是一款可自托管的收藏一切应用链接、笔记与图片其核心价值在于保存内容后自动完成抓取、AI 打标签与全文索引。本文以官方文档 04-architecture.md 为主线结合仓库源码apps/web、apps/workers、packages/shared-server等逐层拆解其整体架构Web 应用如何读写 SQLite、后台 Worker 如何消费任务队列并完成爬取 / AI 推理 / 索引三类任务以及 Meilisearch 与无头浏览器在其中的角色。读完本文你将掌握该项目的组件划分、三类 Worker 任务的完整调用链以及如何通过docker-compose.yml搭建与之匹配的运行拓扑。一、架构总览三层组件与三类任务原文档对架构的概括极为精炼可提炼为两个核心事实Web 应用Webapp基于 Next.js 构建使用 SQLite 存储数据后台 WorkerWorkers消费任务队列中的任务并执行任务分为三类——爬取Crawling、AI 推理OpenAI与索引Indexing。其中爬取任务使用运行在 Worker 容器内的无头 Chrome 浏览器抓取链接内容AI 推理任务调用 AI 接口为内容推断标签以及摘要、向量化等衍生任务索引任务将内容写入 Meilisearch以加速搜索阶段的检索。结合架构图与仓库代码可以还原出更完整的组件图景用户通过 Web App 保存书签Web App 将抓取/推理/索引等异步工作以任务形式投递到队列Worker 消费任务需要时驱动无头浏览器抓取网页或调用 AI 服务最终把结果写回 SQLite、并把可检索内容同步进 Meilisearch。仓库中对应的核心目录为 apps/webWeb 应用、apps/workers/workers各 Worker 实现与 packages/shared-server/src/queues.ts队列定义。版本说明本文关联文档属于version-v0.31.0版本快照其中描述的任务队列基于 SQLite当前主线代码已将队列抽象为可插拔后端详见下文任务队列小节架构图中的 Redis 即反映了队列后端可替换为独立服务的事实。文中引用源码时以当前主线仓库为准。二、Web 应用层Next.js SQLite tRPC2.1 技术栈与数据存储Web 应用位于 apps/web是一个基于 Next.js 的单体应用直接以 SQLite 作为主数据存储。数据库 schema 与迁移集中在 packages/dbschema.ts定义了书签、标签、列表、用户等核心表drizzle/目录下存放了从0000到0025共 26 个增量 SQL 迁移文件说明数据模型在持续演进。从目录结构看02-directories.md大部分业务逻辑并不直接写在 Web 应用里而是收敛在 packages/trpc 的routers/与models/中通过 tRPC 路由暴露给前端——apps/web下的dashboard、settings、reader等页面组件只负责 UI 与调用。这种UI 薄、业务层厚的组织方式使得同一套业务逻辑可以被 Web、移动端apps/mobile等不同前端复用。2.2 Web 与 Worker 的分工边界Web App 的职责是同步响应式的操作创建/更新/删除书签、管理标签与列表、展示搜索结果凡是耗时或依赖外部服务的步骤抓网页、跑 AI、建索引则一律投递到任务队列由 Worker 异步完成。这样既避免了请求阻塞也让抓取、推理等重活可以在独立进程中并行执行。三、任务队列连接 Web 与 Worker 的异步纽带3.1 从 SQLite 队列到可插拔队列后端v0.31.0 文档明确指出任务队列基于 SQLite。在当时的实现中队列任务直接落库Worker 通过轮询 SQLite 表获取待执行任务——这与Web App ↔ SQLite、Workers ↔ SQLite 双向读写的架构图完全吻合。而当前主线在 packages/shared/queueing.ts 中抽象了统一的Queue、EnqueueOptions、getQueueClient等接口并提供了多种可插拔实现仓库 packages/plugins 下包含queue-liteque基于 SQLite/LiteQueue 的队列插件与queue-restate基于 Restate 的分布式队列插件两套后端队列客户端由 packages/plugins/lib 统一导出。因此小型自托管部署默认仍可沿用 SQLite 队列无需额外组件需要更高吞吐与分布式能力时可切换到独立队列服务这正是架构图中 Redis 出现的原因。Worker 的统一消费模型是getQueueClient().createRunner(queue, handlers, options)见 crawlerWorker.tshandlers 包含run、onComplete、onError三个回调并支持pollIntervalMs默认 1000ms、concurrency、timeoutSecs等运行参数。3.2 队列与任务类型的定义队列名称、任务 schema 与默认重试次数统一定义在 packages/shared-server/src/queues.ts从中可以看到任务类型远不止文档列出的三类而是围绕书签生命周期的一组队列队列名称代码常量默认重试消费 Worker爬取队列CrawlQueue—crawlerWorker.tsAI 推理队列OpenAIQueue3 次inferenceWorker.ts向量嵌入队列EmbeddingsQueue3 次embeddingsWorker.ts搜索索引队列SearchIndexingQueue—searchWorker.ts视频下载队列VideoWorkerQueue—videoWorker.ts除此之外apps/workers 下还有backupWorker、feedWorkerRSS 订阅、importWorker导入、ruleEngineWorker自动化规则、webhookWorker、adminMaintenanceWorker等它们共同构成 Worker 进程群入口见 apps/workers/index.ts。四、任务类型一爬取任务Crawling爬取任务由CrawlerWorker消费核心实现位于 crawlerWorker.ts其运行流程充分体现了无头浏览器 分段职责的设计域名限流检查checkDomainRateLimit按 hostname 执行限流命中限流时抛出QueueRetryAfterError并附加 1.01.4 倍随机抖动jitter避免惊群效应crawlerWorker.ts内容探测probe先通过 HTTP 探测 URL 的 content-type 与元数据标题、缩略图等若探测结果为 PDF 或受支持的图片类型则直接把链接书签转为资产asset书签handleAsAssetBookmark浏览器抓取常规网页交由无头 Chrome 执行真实抓取——浏览器生命周期管理在 crawler/browser.ts单页抓取在 crawler/crawlPage.tsHTML 解析在 crawler/parseSubprocess.ts子进程解析避免阻塞主进程资产截图、PDF、全文存档持久化在 crawler/assetStorage.ts投递后续任务抓取成功后enqueuePostCrawlJobs会依据配置链式投递推理打标签/摘要/向量化、搜索索引重建、视频下载与 webhook 通知crawlerWorker.ts。重试语义上Worker 会在失败且重试次数耗尽时将书签的crawlStatus置为failure并清除其taggingStatus、summarizationStatus、embeddingStatus等 pending 状态避免下游任务被悬挂crawlerWorker.ts。无头浏览器的连接方式由BROWSER_WEB_URL/BROWSER_WEBSOCKET_URL等配置决定见 crawler/browser.ts 与 docker-compose.yml 中的BROWSER_WEB_URL: http://chrome:9222仓库还提供了 ad-hoc 爬取 CLIapps/workers/scripts/crawlAdhoc.ts用于脱离队列直接验证真实浏览器抓取路径。五、任务类型二AI 推理任务打标签与摘要5.1 从OpenAI API到可插拔推理客户端v0.31.0 文档将第二类任务描述为使用 OpenAI APIs 推断内容标签。当前实现已将其抽象为InferenceClientFactory位于 packages/shared/inference.ts支持 OpenAI 之外更多兼容端点如 Ollama 等本地模型具体提供商配置见 docs/docs/03-configuration/02-different-ai-providers.md。推理任务由OpenAiWorkerinferenceWorker.ts消费OpenAIQueue按type分发到两种子任务tag执行runTagginginference/tagging.ts基于内容构建提示词从 LLM 响应中解析标签数组响应可能是纯 JSON也可能被包裹在 markdown 代码块中代码里有三级容错解析逻辑见 tagging.ts随后将标签关联到书签summarize执行runSummarizationinference/summarize.ts生成书签摘要。5.2 状态机与幂等标记与爬取任务类似推理 Worker 在onComplete时将书签的taggingStatus/summarizationStatus置为success在重试耗尽后置为failureinferenceWorker.ts。这套pending → success/failure的状态机让 Web 层可以随时展示书签处理进度也让失败任务可以被安全地重新入队。当开启向量检索serverConfig.embedding.enableAutoIndexing时打标签任务会先由EmbeddingsQueue生成嵌入完成后再触发打标签见 crawlerWorker.ts实现语义向量 标签双通道。六、任务类型三索引任务Meilisearch 全文索引第三类任务由SearchIndexingWorkersearchWorker.ts消费SearchIndexingQueue核心逻辑是把书签及其关联数据组装成可检索文档写入 Meilisearch文档组装runIndex从 SQLite 一次性加载书签及其link/text/asset/tagsOnBookmarks关联searchWorker.ts将 URL、标题、描述、正文纯文本、出版社、作者、日期、摘要、标签等字段聚合成BookmarkSearchDocument两类操作index写入/更新文档delete删除文档书签被删除时同步清理索引重试降级仅在首次运行runNumber 0启用批量写入重试时关闭 batching 以提升单条可靠性searchWorker.ts。搜索侧的能力由 packages/shared/search.ts 封装默认实现即 Meilisearch可插拔仓库 packages/plugins/search-meilisearch 中提供了独立插件实现。前端通过搜索队列/搜索接口即可获得全文检索、标签过滤、分页等能力。七、部署拓扑docker-compose 视角下的三服务联动架构图的组件与官方一键部署配置一一对应。查看 docker-compose.yml 可以看到三个核心服务web主应用容器镜像ghcr.io/karakeep-app/karakeep映射宿主机3000端口数据卷挂载到容器内/data即 SQLite 数据目录通过环境变量声明下游依赖MEILI_ADDR: http://meilisearch:7700—— 指向 Meilisearch 服务BROWSER_WEB_URL: http://chrome:9222—— 指向无头 Chrome 服务OPENAI_API_KEY—— AI 推理凭证注释示例可选。chrome无头 Chrome 容器镜像ghcr.io/karakeep-app/karakeep-chrome启动参数包含--disable-gpu、--disable-dev-shm-usage、--window-size1440,900等专供 Worker 抓取页面使用meilisearchMeilisearch v1.41.0独立数据卷持久化索引MEILI_NO_ANALYTICS: true关闭遥测。这套拓扑说明SQLiteweb 容器卷承担主数据存储Meilisearch 承担检索Chrome 承担抓取——三者通过 Docker 内部网络互联与架构图的分工完全一致。单机部署时队列默认落在 SQLite 内无需额外 Redis需要横向扩展时再引入独立队列后端。八、端到端工作流一次保存书签的生命周期综合以上各层可以串出一条完整的时序链路用户在 Web App 保存一个链接书签tRPC 业务层packages/trpc/routers/bookmarks.ts将其写入 SQLite并投递一个爬取任务到队列CrawlerWorker消费任务先做域名限流检查与内容探测若为普通网页则驱动无头 Chrome 抓取解析正文并持久化截图 / PDF / 全文存档等资产结果写回 SQLite抓取完成后链式投递推理任务打标签、摘要、向量化与搜索索引任务OpenAiWorker调用推理客户端生成标签与摘要回写书签行更新taggingStatus/summarizationStatusSearchIndexingWorker将书签聚合为检索文档写入 Meilisearch用户在搜索框输入关键词Web App 通过 Meilisearch 快速检索SQLite 仅负责回读书签明细。任一环节失败都会经过队列重试默认 3 次与状态机标记最终以success/failure状态呈现在书签上整个系统因此具备了异步解耦、失败可重试、进度可观测三个核心特性。九、源码导航建议想要深入架构的读者建议按以下路径阅读仓库Web 应用apps/web/app页面路由与 apps/web/components/dashboard书签管理 UI业务逻辑层packages/trpc/routerstRPC 路由与 packages/trpc/models领域模型与业务服务数据库packages/db/schema.ts表结构与 packages/db/drizzle迁移Worker 群apps/workers/workers各 Worker 实现与 apps/workers/index.ts进程入口队列与共享服务packages/shared-server/src/queues.ts 与 packages/shared/queueing.ts可插拔后端packages/plugins队列 / 搜索 / 存储 / 限流等插件部署编排docker-compose.yml 与 charts、kubernetes。整体而言Karakeep 采用单体 Web 异步 Worker 可插拔中间件的务实架构SQLite 保证了自托管场景的零运维成本Meilisearch 与无头 Chrome 分别补齐了全文检索与真实页面抓取两大硬需求而队列与各服务的插件化抽象则为规模增长预留了升级路径——这正是理解其全部功能AI 自动标签、全文搜索、网页存档、RSS 与导入等的一把总钥匙。【免费下载链接】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),仅供参考
返回列表