昨天我們完成了 Idempotency 冪等性的實作!讓前端在發起請求前先生成唯一的 uploadId,並將此 ID 作為 S3 Key。搭配昨天的 S3 User Metadata 檔名還原與 Semaphore 控流閘,我們的應用層三層防禦大功告成!
這時候,我們再來思考一個問題:雖然在 Java 程式碼層面成功防堵了重複物件的產生,但那些在網路超時(Timeout)斷線、被使用者中途放棄的上傳垃圾,以及重試覆蓋後的歷史版本,現在到底流落到哪裡去了?
今天我們不寫任何 Java 程式碼,把視角拉高到 雲端基礎設施維運(Cloud Infrastructure & DevOps)的層次。我們將深入 AWS 雲端,實戰配置 S3 物件版本控制(Versioning)與生命週期規則(Lifecycle Rules),實現 0 開發成本、0 系統負載,由 AWS 官方在背景幫我們自動消滅儲存計費吸血鬼的防禦!
它們兩者在分散式架構中解決的是不同維度的問題,兩者是互補的共生關係:
因此,最完美的解法是用 Java 程式在當下防衛主動自我修復,用 S3 託管規則在事後 0 成本自動清理!
雲端服務的設定中有很多眉眉角角,有時候調整了一些參數設定可以在效能不變很多的狀況下大幅降低成本,例如我們在建立service或是VM的時候其實可以把最低核心數量設為0(通常預設為1或他會建議你至少要寫1,但根據個人需求有時候其實不用),s3 的儲存服務也是一樣,我們先來看一下他的計費方式:
| 計費項目 | 計費維度 | 東京區域標準儲存單價 (USD) | 備註 |
|---|---|---|---|
| 儲存空間費 (Storage) | 每月儲存總 GB 數 | $0.023 / GB | 歷史版本與分段碎片皆算入此總量 |
| Class A 請求費 (API) | PUT, COPY, POST, LIST | $0.005 / 1,000 次 | 費用極高!用 Java 寫 LIST 掃描是預算大忌 |
| Class B 請求費 (API) | GET, HEAD, Select | $0.0004 / 10,000 次 | 費用極低,double check 選 HEAD 的原因 |
| 資料傳出費 (Data Transfer) | 傳出到 Internet 的總 GB | $0.09 / GB | 傳入(Inbound)至 S3 是免費的 |
在這個計費結構下,如果我們放任系統累積以下兩種隱形垃圾,雲端帳單很快就會失控:
吸血鬼一號:被隱藏的歷史版本(Noncurrent Versions)
吸血鬼二號:大檔案傳輸中斷留下的未完成分段上傳(Incomplete Multipart Uploads)
Versioning 是 Amazon S3 的一項功能,用來保留、擷取和還原 S3 儲存貯體(Bucket)中每個物件(Object)的所有版本,有以下特點 :
由測試內容可以看到我們上傳兩次id結尾是11的檔案,然後透過查詢發現s3只有一筆資料
從aws console來看也只有一筆檔案
進入AWS S3 Console,點擊你的 Bucket,進入 Properties (屬性) 頁籤。
找到 Bucket Versioning (儲存貯體版本控制),點擊 Edit,將其改為 Enable (啟用) 並儲存。
但是從aws console來看就可以看到有版本迭代的紀錄!
透過前面兩個實驗可以發現,雖然不啟用 Versioning 似乎可以直接省去歷史版本清理的麻煩,但在企業級高安全規格的生產環境中,為了防範人為手殘誤刪、或是駭客勒索軟體惡意篡改,100% 的 S3 儲存桶都會被資安政策強制要求開啟 Versioning!
現在,讓我們點開 AWS S3 控制台,親手為我們的儲存桶拉起這條 0 運維成本的生命週期自癒防線。

這條規則能徹底防堵斷線遺留的隱形碎片計費黑洞:
如果你的 Bucket 開啟了版本控制(Versioning),我們必須確保重試產生的舊版本不會無限期霸佔空間:
如此一來時間到之後s3就會自動幫你清理一些孤兒檔案啦!
在現代高規格的雲端原生(Cloud Native)維運中,資深架構師極少手動登入網頁點擊配置。我們通常會使用 IaC(基礎設施即代碼,如 Terraform 或 AWS CloudFormation),或在初始化專案時直接呼叫 API 來套用規則。
以下是我們在背景默默運行的這兩條生命週期防禦線的標準 JSON 配置。看懂這個底層結構,能讓你對 S3 的微觀運行邏輯有更精準的掌控力:
{
"Rules": [
{
"ID": "CleanIncompleteMultipartUploads",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
},
{
"ID": "ExpireNoncurrentVersions",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"NoncurrentVersionExpiration": {
"NoncurrentDays": 3,
"NewerNoncurrentVersions": 1
}
}
]
}
AbortIncompleteMultipartUpload 刪除: DaysAfterInitiation: 7 代表 AWS S3 每天會在背景定時掃描。只要發現某個大檔案上傳任務發起超過 7 天卻依然處於 Incomplete(未完成)狀態,底層機制就會自動將其所有隱形碎片徹底抹除,連 1 KB 的快取都不留給你計費。
NoncurrentVersionExpiration 收斂: 搭配我們 Day 12 的冪等重試,NoncurrentDays: 3 與 NewerNoncurrentVersions: 1 的組合堪稱完美。當檔案被新版本覆蓋後,最多只幫我保留 1 個最近的舊版備份容錯。其餘更舊的歷史版本,請在 3 天後自動永久刪除。這在資料安全與儲存成本之間,取得了最完美的平衡。
今天,我們跳脫純程式碼開發,以一個更宏觀的視角,完成了我們微服務背後最關鍵的基礎設施自動化清理機制。並且學習到:
明天我們將在本地開發環境中,引入混沌工程(Chaos Engineering),教大家如何用 Linux tc 與 macOS pfctl 等工具,親手把本機的網路卡模擬成惡劣網路環境,來對微服務進行更貼近真是現況測試!