Как разработчики, мы часто сталкиваемся с проблемами при крупномасштабной обработке и доставке данных. В Kamero мы недавно устранили серьезное узкое место в нашем конвейере доставки файлов. Наше приложение позволяет пользователям загружать тысячи файлов, связанных с определенным событием, в виде одного zip-файла. Эта функция, основанная на функции Lambda на основе Node.js, отвечающей за выборку и сжатие файлов из корзин S3, столкнулась с ограничениями памяти и длительным временем выполнения по мере роста нашей пользовательской базы.
В этом посте подробно описан наш путь от ресурсоемкой реализации Node.js к экономичному и молниеносному решению Go, которое эффективно справляется с массовыми загрузками S3. Мы рассмотрим, как мы оптимизировали нашу систему, чтобы обеспечить пользователям удобство работы при запросе большого количества файлов с определенных событий, упакованных в один удобный архив для загрузки.
Наша исходная функция Lambda столкнулась с несколькими критическими проблемами при обработке больших наборов файлов на основе событий:
Наша первоначальная реализация использовала библиотеку s3-zip для создания zip-файлов из объектов S3. Вот упрощенный фрагмент того, как мы обрабатывали файлы:
const s3Zip = require("s3-zip"); // ... other code ... const body = s3Zip.archive( { bucket: bucketName }, eventId, files, entryData ); await uploadZipFile(Upload_Bucket, zipfileKey, body);
Хотя этот подход работал, он загружал все файлы в память перед созданием zip-архива, что приводило к интенсивному использованию памяти и возможным ошибкам нехватки памяти для больших наборов файлов.
Мы решили переписать нашу функцию Lambda на Go, максимально используя ее эффективность и встроенные функции параллелизма. Результаты были ошеломляющими:
Мы использовали AWS SDK для Go v2, который обеспечивает лучшую производительность и меньшее использование памяти по сравнению с v1:
cfg, err := config.LoadDefaultConfig(context.TODO()) s3Client = s3.NewFromConfig(cfg)
Горутины Go позволили нам обрабатывать несколько файлов одновременно:
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Этот подход позволяет нам обрабатывать несколько файлов одновременно, контролируя при этом уровень параллелизма, чтобы не перегружать систему.
3. Потоковое создание Zip
Вместо того, чтобы загружать все файлы в память, мы передаем содержимое zip-архива непосредственно на 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, })Такой подход потоковой передачи значительно снижает использование памяти и позволяет нам обрабатывать гораздо большие наборы файлов.
Результаты
Переписывание на Go привело к впечатляющим улучшениям:
- Использование памяти: уменьшено на 99 % (с 10 ГБ до 100 МБ)
- Скорость обработки: увеличена примерно на 1000 %
- Надежность: успешно обрабатывает 20 000 файлов без проблем
- Эффективность затрат: меньшее использование памяти и более быстрое время выполнения приводят к снижению затрат на AWS Lambda
Извлеченные уроки
- Выбор языка имеет значение: модель эффективности и параллелизма Go существенно изменила наш вариант использования.
- Определите свои узкие места: профилирование нашей функции Node.js помогло нам определить ключевые области для улучшения.
- Используйте облачные решения: использование AWS SDK для Go v2 и понимание возможностей S3 позволили улучшить интеграцию и повысить производительность.
- Думайте в потоках: обработка данных в виде потоков, а не загрузка всего в память, имеет решающее значение для крупномасштабных операций.
Заключение
Переписывание нашей лямбда-функции в Go не только решило наши непосредственные проблемы с масштабированием, но также предоставило более надежное и эффективное решение для наших потребностей в обработке файлов. Хотя Node.js поначалу сослужил нам хорошую службу, этот опыт подчеркнул важность выбора правильного инструмента для работы, особенно при решении масштабных ресурсоемких задач.
Помните, что лучший язык или фреймворк зависит от вашего конкретного варианта использования. В нашем сценарии характеристики производительности Go идеально соответствовали нашим потребностям, что привело к значительному улучшению пользовательского опыта и снижению эксплуатационных расходов.
Сталкивались ли вы с подобными проблемами при использовании бессерверных функций? Как вы их преодолели? Мы будем рады услышать о вашем опыте в комментариях ниже!
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3