身為開發人員,我們在處理大規模資料處理和交付時經常面臨挑戰。在 Kamero,我們最近解決了文件傳輸管道中的一個重大瓶頸。我們的應用程式允許用戶將與特定事件相關的數千個檔案下載為單一 zip 檔案。此功能由基於 Node.js 的 Lambda 函數提供支持,負責從 S3 存儲桶中獲取和壓縮文件,但隨著我們用戶群的增長,該功能一直面臨著內存限制和較長執行時間的問題。
這篇文章詳細介紹了我們從資源匱乏的 Node.js 實現到高效處理大量 S3 下載的精益且快速的 Go 解決方案的歷程。我們將探討如何優化我們的系統,以便在從特定事件請求大量文件時為用戶提供無縫體驗,所有文件都打包到一個方便的單一 zip 下載中。
我們最初的 Lambda 函數在處理基於事件的大型檔案集時面臨幾個關鍵問題:
我們最初的實作使用 s3-zip 函式庫從 S3 物件建立 zip 檔案。這是我們如何處理文件的簡化片段:
const s3Zip = require("s3-zip"); // ... other code ... const body = s3Zip.archive( { bucket: bucketName }, eventId, files, entryData ); await uploadZipFile(Upload_Bucket, zipfileKey, body);
雖然這種方法有效,但它會在創建 zip 之前將所有檔案載入到記憶體中,從而導致記憶體使用率較高,並且大型檔案集可能會出現記憶體不足錯誤。
我們決定用 Go 重寫 Lambda 函數,利用其效率和內建並發功能。結果令人震驚:
我們使用了 AWS SDK for Go v2,與 v1 相比,它提供了更好的效能和更低的記憶體使用量:
cfg, err := config.LoadDefaultConfig(context.TODO()) s3Client = s3.NewFromConfig(cfg)
Go 的 goroutine 允許我們同時處理多個檔案:
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()
這種方法允許我們同時處理多個文件,同時控制並發等級以防止系統不堪重負。
我們不是將所有檔案載入到記憶體中,而是將 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 的重寫帶來了令人印象深刻的改進:
用 Go 重寫 Lambda 函數不僅解決了我們眼前的擴充問題,也為我們的檔案處理需求提供了更強大、更有效率的解決方案。雖然 Node.js 最初為我們提供了良好的服務,但這次經驗凸顯了為工作選擇正確工具的重要性,尤其是在處理大規模資源密集型任務時。
請記住,最好的語言或框架取決於您的特定用例。在我們的場景中,Go 的效能特徵與我們的需求完美契合,從而顯著改善了使用者體驗並降低了營運成本。
您在無伺服器功能方面是否遇到類似的挑戰?你是如何克服它們的?我們很樂意在下面的評論中聽到您的經歷!
以上是從 Node.js 到 Go:以單一 Zip 方式增強數千個檔案的下載量的詳細內容。更多資訊請關注PHP中文網其他相關文章!