En tant que développeurs, nous sommes souvent confrontés à des défis lorsque nous traitons du traitement et de la livraison de données à grande échelle. Chez Kamero, nous avons récemment résolu un goulot d'étranglement important dans notre pipeline de livraison de fichiers. Notre application permet aux utilisateurs de télécharger des milliers de fichiers associés à un événement particulier sous la forme d'un seul fichier zip. Cette fonctionnalité, optimisée par une fonction Lambda basée sur Node.js, responsable de la récupération et de la compression des fichiers à partir des compartiments S3, était confrontée à des contraintes de mémoire et à de longs temps d'exécution à mesure que notre base d'utilisateurs grandissait.
Cet article détaille notre parcours depuis une implémentation Node.js gourmande en ressources jusqu'à une solution Go légère et ultra-rapide qui gère efficacement les téléchargements S3 massifs. Nous explorerons comment nous avons optimisé notre système pour offrir aux utilisateurs une expérience transparente lors de la demande d'un grand nombre de fichiers provenant d'événements spécifiques, le tout regroupé dans un seul téléchargement zip pratique.
Notre fonction Lambda d'origine était confrontée à plusieurs problèmes critiques lors du traitement de grands ensembles de fichiers basés sur des événements :
Notre implémentation originale utilisait la bibliothèque s3-zip pour créer des fichiers zip à partir d'objets S3. Voici un extrait simplifié de la façon dont nous traitions les fichiers :
const s3Zip = require("s3-zip"); // ... other code ... const body = s3Zip.archive( { bucket: bucketName }, eventId, files, entryData ); await uploadZipFile(Upload_Bucket, zipfileKey, body);
Bien que cette approche ait fonctionné, elle a chargé tous les fichiers en mémoire avant de créer le zip, ce qui a entraîné une utilisation élevée de la mémoire et des erreurs potentielles de manque de mémoire pour les grands ensembles de fichiers.
Nous avons décidé de réécrire notre fonction Lambda dans Go, en tirant parti de son efficacité et de ses fonctionnalités de concurrence intégrées. Les résultats ont été stupéfiants :
Nous avons utilisé le SDK AWS pour Go v2, qui offre de meilleures performances et une utilisation moindre de la mémoire par rapport à la v1 :
cfg, err := config.LoadDefaultConfig(context.TODO()) s3Client = s3.NewFromConfig(cfg)
Les goroutines de Go nous ont permis de traiter plusieurs fichiers simultanément :
var wg sync.WaitGroup sem := make(chan struct{}, 10) // Limit concurrent operations for _, photo := range photos { wg.Add(1) go func(photo Photo) { defer wg.Done() sem <- struct{}{} // Acquire semaphore defer func() { <-sem }() // Release semaphore // Process photo }(photo) } wg.Wait()
Cette approche nous permet de traiter plusieurs fichiers simultanément tout en contrôlant le niveau de concurrence pour éviter de surcharger le système.
Au lieu de charger tous les fichiers en mémoire, nous diffusons le contenu zip directement sur S3 :
pipeReader, pipeWriter := io.Pipe() go func() { zipWriter := zip.NewWriter(pipeWriter) // Add files to zip zipWriter.Close() pipeWriter.Close() }() // Upload streaming content to S3 uploader.Upload(ctx, &s3.PutObjectInput{ Bucket: &destBucket, Key: &zipFileKey, Body: pipeReader, })
Cette approche de streaming réduit considérablement l'utilisation de la mémoire et nous permet de gérer des ensembles de fichiers beaucoup plus volumineux.
La réécriture vers Go a apporté des améliorations impressionnantes :
La réécriture de notre fonction Lambda dans Go a non seulement résolu nos problèmes de mise à l'échelle immédiats, mais a également fourni une solution plus robuste et plus efficace pour nos besoins de traitement de fichiers. Bien que Node.js nous ait bien servi au départ, cette expérience a mis en évidence l'importance de choisir le bon outil pour le travail, en particulier lorsqu'il s'agit de tâches gourmandes en ressources à grande échelle.
N'oubliez pas que le meilleur langage ou framework dépend de votre cas d'utilisation spécifique. Dans notre scénario, les caractéristiques de performance de Go correspondaient parfaitement à nos besoins, ce qui se traduisait par une expérience utilisateur considérablement améliorée et des coûts opérationnels réduits.
Avez-vous été confronté à des défis similaires avec les fonctions sans serveur ? Comment les avez-vous surmontés ? Nous serions ravis de connaître vos expériences dans les commentaires ci-dessous !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!