Maison  >  Article  >  développement back-end  >  De Node.js à Go : surchargez les téléchargements de milliers de fichiers en un seul zip

De Node.js à Go : surchargez les téléchargements de milliers de fichiers en un seul zip

WBOY
WBOYoriginal
2024-08-21 12:32:40824parcourir

From Node.js to Go: Supercharging Sownloads of Thousands of Files as a Single Zip

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.

Le défi

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 :

  1. Consommation de mémoire : même avec 10 Go de mémoire allouée, la fonction échouerait lors du traitement de plus de 20 000 fichiers pour des événements plus importants.
  2. Durée d'exécution : les opérations Zip pour les événements comportant de nombreux fichiers prenaient trop de temps, parfois avant la fin.
  3. Évolutivité : la fonction ne pouvait pas gérer efficacement la charge croissante, limitant notre capacité à servir les utilisateurs disposant de grands ensembles de fichiers provenant d'événements populaires.
  4. Expérience utilisateur : les temps de préparation des téléchargements lents avaient un impact sur la satisfaction des utilisateurs, en particulier pour les événements comportant un nombre de fichiers important.

L'implémentation de Node.js : un aperçu rapide

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.

Enter Go : une réécriture qui change la donne

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 :

  1. Utilisation de la mémoire : passée de 10 Go à seulement 100 Mo pour la même charge de travail.
  2. Vitesse : La fonction est devenue environ 10 fois plus rapide.
  3. Fiabilité : traite avec succès plus de 20 000 fichiers sans problème.

Optimisations clés dans la mise en œuvre Go

1. Opérations S3 efficaces

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)

2. Traitement simultané

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.

3. Création de fichiers Zip en streaming

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.

Les résultats

La réécriture vers Go a apporté des améliorations impressionnantes :

  1. Utilisation de la mémoire : réduite de 99 % (de 10 Go à 100 Mo)
  2. Vitesse de traitement : augmentée d'environ 1 000 %
  3. Fiabilité : gère avec succès plus de 20 000 fichiers sans problème
  4. Efficacité des coûts : une utilisation moindre de la mémoire et un temps d'exécution plus rapide entraînent une réduction des coûts AWS Lambda

Leçons apprises

  1. Le choix de la langue est important : le modèle d'efficacité et de concurrence de Go a fait une énorme différence dans notre cas d'utilisation.
  2. Comprenez vos goulots d'étranglement : le profilage de notre fonction Node.js nous a aidé à identifier les domaines clés à améliorer.
  3. Tirer parti des solutions cloud natives : L'utilisation du SDK AWS pour Go v2 et la compréhension des capacités de S3 ont permis une meilleure intégration et de meilleures performances.
  4. Pensez en flux : traiter les données sous forme de flux plutôt que de tout charger en mémoire est crucial pour les opérations à grande échelle.

Conclusion

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn