Python 异步漫画解析器:从架构到微服务
构建一个可扩展的漫画解析器是一项结合了异步编程、契约设计和状态管理的挑战性任务。Manga-Day 是一个从单体架构向微服务架构转型的实际案例,使用了 asyncio、Pydantic 和 SQLAlchemy。该项目最初构想为一个自动从数十个网站收集数据的工具,但如今已成为设计可扩展系统的教程。
架构基础:契约与异步性
系统的核心围绕抽象类 BaseSpider 构建。每个具体的解析器(例如,用于 multi-manga 的解析器)只需实现单一接口,即可动态添加新数据源,而无需修改核心代码。契约包括三个关键方法:
from abc import ABC, abstractmethod
class BaseSpider(ABC):
BASE_URL = "https://example-manga.com"
@abstractmethod
async def get(self, url: str, **kwargs) -> Optional[MangaSchema]: ...
@abstractmethod
async def pages(
self, start_page: int | None = None
) -> AsyncGenerator[list[BaseManga], Any]: ...
这种方法确保了:
- 统一的错误和超时处理
- 能够独立测试每个 spider
- 通过异步生成器支持水平扩展
异步性在 asyncio 层面实现,能够高效处理数百个并发请求,而不会因 I/O 阻塞。这在处理缓慢或不稳定的数据源时尤为重要。
数据验证与 ORM 模型
Pydantic 用于严格的类型化和验证。系统基于三个层次的模型构建:
- BaseManga — 最小字段集:标题、海报、URL、sku(SHA256 哈希)
- MangaWithGallery — 添加图像 URL 列表
- MangaSchema — 完整模型,包括类型、作者和语言(可选)
在 SQLAlchemy 管理的数据库中,实现了以下实体:
- Manga、Author、Genre、Language — 核心表
- GenreManga — 类型间的多对多关系
- Gallery — 图像的独立表(避免随主查询加载)
- GeneratedPdf — 已弃用的表,计划移除
将图库分离为独立实体,避免了加载漫画列表时的 N+1 问题——图像现仅在明确请求时加载。
管理器:数据之上的业务逻辑
管理器充当业务逻辑层——负责特定操作的类:
- MangaManager:基于 SKU、URL、ID 的 CRUD 操作
- RequestManager:带重试逻辑和超时的 HTTP 请求
- AlertManager:通用通知系统(Telegram、Discord 等)
- SpiderManager:所有 spider 的中央协调器
SpiderManager 因其模块化设计而特别有趣。它被拆分为四个文件:
_load.py— 通过__all__动态加载 spider_status.py— 状态模型和枚举_starter.py— 启动和生命周期管理_spider.py— 将组件组合成单一接口
这种拆分简化了测试,并允许变更隔离——例如,添加新 spider 仅需在 __init__.py 中注册,无需修改核心代码。
向微服务转型:痛点与解决方案
项目起初是单体架构,但两个服务成为变革的催化剂:
- PDFService — 消耗过多 RAM,迁移到独立容器
- Bot — 需要 WebSocket 和 JWT 认证,也迁移到隔离服务
迁移经历了几个阶段:
- 前端与核心解耦——现仅通过 REST API 工作,Jinja 仅用于配置
- Bot 使用 aiohttp 重写,支持 WebSocket 并通过 JWT 实现基于角色的访问控制
- PDF 生成现作为后台任务运行,并限制内存使用
主要挑战:
- 服务间复杂对象的序列化
- 部分故障时的數據一致性
- 设置健康检查和优雅关机
关键经验
- 契约比实现更重要 — 抽象类实现了无需核心重构的扩展
- 异步 ≠ 自动性能提升 — 控制并发任务数并使用信号量
- ORM 并非万能 — 手动查询优化和反范式化(例如分离图库)对性能至关重要
- 微服务增加复杂度但提供灵活性 — 将 PDFService 独立解决了内存问题,但提升了部署复杂度
- 可测试性是架构成果 — SpiderManager 的模块化允许无需模拟外部依赖的单元测试
该项目已成为理解如何构建可长期演进系统的起点。代码库已扩展到数万行,但清晰的责任分离保持了其可维护性。下一步包括实现消息代理用于任务队列,并在每个拉取请求上运行完整的 CI/CD 和自动测试。
— Editorial Team
暂无评论。