# Parseur de mangas asynchrone en Python : De l'architecture aux microservices
Construire un parseur de mangas scalable est une tâche qui combine les défis de la programmation asynchrone, de la conception de contrats et de la gestion d'état. Manga-Day est un exemple concret de transition d'un monolithe vers une architecture de microservices en utilisant asyncio, Pydantic et SQLAlchemy. Le projet a été initialement conçu comme un outil pour collecter automatiquement des données sur des dizaines de sites, mais il est devenu un tutoriel sur la conception de systèmes extensibles.
Fondations architecturales : Contrats et asynchronicité
Le cœur du système repose sur la classe abstraite BaseSpider. Chaque parseur spécifique (par exemple, pour multi-manga) implémente une interface unique, permettant d'ajouter dynamiquement de nouvelles sources sans modifier le noyau. Le contrat comprend trois méthodes clés :
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]: ...
Cette approche garantit :
- Une gestion uniforme des erreurs et des timeouts
- La possibilité de tester chaque spider indépendamment
- Le support de l'évolutivité horizontale grâce aux générateurs asynchrones
L'asynchronicité est implémentée au niveau de asyncio, permettant une gestion efficace de centaines de requêtes concurrentes sans blocage d'E/S. Cela est particulièrement important lors du travail avec des sources lentes ou instables.
Validation des données et modèles ORM
Pydantic est utilisé pour un typage strict et une validation. Le système repose sur trois niveaux de modèles :
- BaseManga — ensemble minimal de champs : titre, poster, url, sku (hachage SHA256)
- MangaWithGallery — ajoute une liste d'URL d'images
- MangaSchema — modèle complet avec genres, auteur et langue (optionnelle)
Dans la base de données gérée par SQLAlchemy, les entités suivantes sont implémentées :
- Manga, Author, Genre, Language — tables principales
- GenreManga — relation many-to-many pour les genres
- Gallery — table séparée pour les images (pour éviter de la charger avec la requête principale)
- GeneratedPdf — table obsolète, prévue pour suppression
Séparer la galerie en entité distincte a évité les problèmes N+1 lors du chargement des listes de mangas — les images ne sont désormais chargées que sur demande explicite.
Managers : Logique métier au-dessus des données
Les managers servent de couche de logique métier — des classes responsables d'opérations spécifiques :
- MangaManager : opérations CRUD par SKU, URL, ID
- RequestManager : requêtes HTTP avec logique de retry et timeouts
- AlertManager : système de notification universel (Telegram, Discord, etc.)
- SpiderManager : orchestrateur central pour tous les spiders
SpiderManager est particulièrement intéressant grâce à sa modularité. Il est réparti en quatre fichiers :
_load.py— chargement dynamique des spiders via__all___status.py— modèles de statut et enums_starter.py— lancement et gestion du cycle de vie_spider.py— combinaison des composants en une interface unique
Cette décomposition a simplifié les tests et permis d'isoler les changements — par exemple, ajouter un nouveau spider ne nécessite que son enregistrement dans __init__.py, sans modifications du noyau.
Transition vers les microservices : Points de douleur et solutions
Le projet a commencé comme un monolithe, mais deux services ont été les catalyseurs du changement :
- PDFService — consommait trop de RAM, déplacé dans un conteneur séparé
- Bot — nécessitait WebSocket et authentification JWT, également migré vers un service isolé
La migration s'est déroulée en plusieurs étapes :
- Frontend découplé du noyau — fonctionne désormais exclusivement via REST API, avec un Jinja minimal uniquement pour les configurations
- Bot réécrit en utilisant aiohttp pour WebSocket et implémentation d'un accès basé sur les rôles via JWT
- Génération PDF désormais exécutée comme tâche en arrière-plan avec une utilisation mémoire limitée
Principaux défis :
- Sérialisation d'objets complexes entre services
- Cohérence des données lors de pannes partielles
- Mise en place de health checks et d'arrêt gracieux
Enseignements clés
- Les contrats priment sur l'implémentation — les classes abstraites ont permis l'évolutivité sans refactorisation du noyau
- Asynchronicité ≠ performance automatique — contrôlez le nombre de tâches concurrentes et utilisez des sémaphores
- ORM n'est pas une solution miracle — l'optimisation manuelle des requêtes et la dénormalisation (par ex., séparation de la galerie) sont cruciales pour les performances
- Les microservices ajoutent de la complexité mais offrent de la flexibilité — sortir PDFService a résolu le problème de mémoire mais a augmenté la complexité de déploiement
- La testabilité est un résultat architectural — la modularité de SpiderManager a permis des tests unitaires sans mocking de dépendances externes
Le projet est devenu un point de départ pour comprendre comment construire des systèmes qui peuvent évoluer pendant des années. Le codebase a atteint des dizaines de milliers de lignes, mais une séparation claire des responsabilités le maintient maintenable. Les prochaines étapes incluent l'implémentation d'un courtier de messages pour les files de tâches et une CI/CD complète avec autotests sur chaque pull request.
— Editorial Team
Aucun commentaire pour le moment.