前陣子我在開發一個 AI 應用時,使用 AWS API Gateway 和 Lambda 作為 API,去接 AWS Bedrock 的 LLM 模型,由於思考過程以及中間可能還會去呼叫其他協作工具,使用者等待的時間有可能長達數十秒,只用一個 Loading 的訊息在前端提示,UX 顯然不是很好。
現在的 ChatGPT Like 的介面,都會顯示進度訊息,像是「正在搜尋網站」、「正在匯整資料」,我覺得對於使用者來說,等待的時間不會減少,但由於不斷有動態回傳,心理等待時間會縮短。
當我想實作這個功能時,發現既有的 API Gateway + Lambda 架構存在兩個無法解決的問題:
首先,API Gateway 不支援串流回應。它的 LAMBDA_PROXY 整合會緩衝 Lambda 的所有回應,等全部完成後才一次傳給客戶端。即使 Lambda 本身在 2023 年就已經支援 Response Streaming,透過 API Gateway 調用時這個能力也完全無法發揮。
其次,API Gateway 有 29 秒的超時限制。當 AI 需要呼叫多個工具處理複雜任務時,很容易超過這個時間,連線就會被中斷。
在和 Claude Code 幾輪問答後,我發現需要的是 AWS 的 Function URL 機制——它能繞過 API Gateway,讓 Lambda 的串流能力直接發揮作用。
邊做邊回報背後的 SSE 機制
在討論 Function URL 之前,我們需要先理解 SSE(Server-Sent Events)。
SSE 其實是個「老技術」——它在 HTML5 時代就存在了。簡單來說,SSE 是一種讓伺服器能夠「主動推送」資料給瀏覽器的技術。
傳統的 HTTP 請求是一去一回,你輸入網址,網頁就整頁回傳下載,非常單純。
SSE 的行為則有點像是訂閱電子報:你訂閱一次(建立連線),之後每當有新內容,電子報服務就會自動推送新的一期給你,直到你取消訂閱(關閉連線)。
SSE vs WebSocket
說到長時間連線,通常大家就會想到 WebSocket,為什麼不用 WebSocket?
WebSocket 支援雙向通訊——就像電話,雙方都能隨時說話。但對於我的 AI 應用場景,我其實不需要「對話」,我只需要接收「AI 說的話」。這種情境下,SSE 反而更適合:
- 實作簡單:瀏覽器內建
EventSourceAPI,不需要額外的 library - 自動重連:連線斷了會自動重新連接,不需要自己寫邏輯
- 基於 HTTP:可以利用現有的 HTTP 基礎設施
想像你看著台積電的股價今天又飆升到哪裡,你需要不斷接收最新價格(單向),但你不需要回傳任何資料給伺服器。
同樣的,AI 生成回應時,我只需要接收進度更新,不需要中途回傳資料。
SSE 剛剛好。
Lambda 的兩條路:API Gateway 還是 Function URL?
當我遇到實作問題時,才知道原來 Lambda 有兩種對外提供服務的方式。
API Gateway
API Gateway 可能是大家最熟悉的對外通路,它就像一個超級管家,可以處理各種雜事:
- 身份驗證(支援 API Key、Cognito、自訂授權器)
- 速率限制(防止被濫用)
- 請求驗證(確保資料格式正確)
- 快取(提升效能)
但管家有自己的做事方式:29 秒的超時限制,而且不支援串流回應。
這不是缺陷,而是設計考量。API Gateway 的 LAMBDA_PROXY 整合會緩衝 Lambda 的回應,等到全部準備好才一次傳給客戶端。就像去餐廳吃套餐一樣,要等所有菜都擺好了才一起上桌。對很多應用來說,這樣做有它的道理。但對我需要的「邊做邊回報」,它就幫不上忙了。
(補充:AWS 在 2024 年 6 月開始允許 REST API 申請提高超時限制,但需要權衡 throttle quota,設定也較複雜)
Function URL
Lambda Function URL 是 AWS 在 2022 年 4 月推出的新功能,設計理念完全不同。
它就像個在快炒店吃飯一樣,哪道菜炒好了不管三七二十一就立刻上桌。
優勢:
- 內建 HTTPS 端點,設定簡單
- 無額外費用(只計算 Lambda 本身)
- 支援串流回應
- 15 分鐘超時(vs API Gateway 的 29 秒)
限制:
- 認證方式只有兩種:公開或 AWS IAM
- 沒有內建的速率限制、快取等功能
- 無法自訂網域名稱(URL 包含隨機 ID)
對我的需求來說,Function URL 的優勢正是我需要的:支援串流,而且時間足夠。
但它的限制也很明顯:沒有安全防護、沒有自訂網域。這些問題,就需要其他服務來補足。
Lambda Streaming:原來 Lambda 早就會「邊做邊說」
理解了為什麼要用 Function URL,接下來要搞懂 Lambda 是怎麼做到串流的。
Lambda 在 2023 年推出了 Response Streaming 功能,能夠部分回傳資料,不用等到全部完成。問題是,這個能力只能透過 Function URL 或 AWS SDK 的 InvokeWithResponseStream API 使用,透過 API Gateway 的 LAMBDA_PROXY 會卡住用不了。
這也是為什麼我們需要 Function URL,它讓 Lambda 的串流能力得以發揮。
實際運作方式
在程式碼層面,差異在於使用 awslambda.streamifyResponse() 這個特殊的包裝器:
// 傳統方式
export async function handler(event) {
const result = await processAllData(); // 等待所有資料
return { statusCode: 200, body: result }; // 一次回傳
}
// Streaming 方式
export const handler = awslambda.streamifyResponse(
async (event, responseStream) => {
responseStream.setContentType("text/event-stream");
responseStream.write('data: {"status":"searching"}\n\n');
await searchWeb();
responseStream.write('data: {"status":"processing"}\n\n');
await processData();
responseStream.write('data: {"status":"complete"}\n\n');
responseStream.end();
}
);
重點是 responseStream 物件,每次呼叫 write() 時,資料會立即透過 HTTP Chunked Transfer Encoding 傳送給客戶端。
這個機制帶來兩個重要好處:
- 改善使用者體驗:TTFB(Time to First Byte)從可能的 30 秒降到 1 秒內
- 支援大型回應:最大回應從 6 MB 提升到 200 MB
對 AI 應用來說,這意味著使用者不用呆等 30 秒後才看到回應,而是立刻就能看到「AI 正在思考」的進度。
如果客戶端網速很慢,Lambda 會不會要一直等它傳完才能結束嗎?那不就會超時?
其實不會。Lambda Runtime 會進行緩衝,函數執行時間只計算到 responseStream.end() 呼叫為止。後續傳輸花多久時間,都不影響計費和超時。
用 CloudFront 和 WAF 補上缺失的功能與安全性
到目前為止,Function URL + Streaming 已經能解決我的核心需求了。但還有三個問題:
- Function URL 的網址很醜(隨機 ID)
- 沒有 DDoS 防護和速率限制
- 全球存取延遲可能很高
這就是為什麼要加上 CloudFront 和 WAF。
CloudFront:全球快遞網
CloudFront 是 AWS 的 CDN 服務,使用它有以下的好處:
- 自訂網域:可以指定使用自己的網域名稱
- 全球加速:使用者會連到最近的 CloudFront 節點,延遲更低
- SSL/TLS:自動處理 HTTPS 憑證
- DDoS 防護:內建 AWS Shield Standard,免費防禦基本攻擊
WAF
WAF(Web Application Firewall)則像是門禁系統,幫你擋掉惡意流量:
- 速率限制:例如限制每個 IP 每 5 分鐘最多 2000 個請求
- Bot 防護:AWS Managed Bot Control 能辨識並阻擋機器人
- 攻擊防護:AWS Managed Rules 能防禦常見的 Web 攻擊
完整的架構流程
最終的架構長這樣:
flowchart TD
A["**使用者**"] -->|"① HTTPS 請求<br/>(自訂網域)"| B["**CloudFront + WAF**<br/>速率限制 / Bot 防護"]
B -->|"② OAC 認證"| C["**Lambda Function URL**"]
C -->|"③ 調用函數"| D["**Lambda 函數**"]
D -.->|"④ 串流回應"| C
C -.->|"⑤ 回傳資料"| B
B -.->|"⑥ SSE 串流"| A
style B fill:#FF9900,stroke:#FF9900,color:#ffffff
style C fill:#FF9900,stroke:#FF9900,color:#ffffff
style D fill:#FF9900,stroke:#FF9900,color:#ffffff
關鍵的是 Origin Access Control(OAC):這個機制確保只有 CloudFront 能存取你的 Function URL,外部無法直接訪問。這縮小了攻擊面,所有流量都必須經過 CloudFront 的安全檢查。
與 S3 SPA 的整合
如果前端是放在 S3 的單頁應用(SPA),這個架構還有個額外好處:你可以用同一個 CloudFront distribution 處理:
/→ S3 Bucket(靜態網站)/api/*→ Lambda Function URL(API 端點)
一個網域解決所有問題。
SSE 架構成本會不會很高?
多加了 CloudFront 和 WAF,成本會增加多少?我自己也很在意這點。
成本拆解
Lambda Function URL:
- Function URL 本身:免費
- Lambda 執行:每月前 100 萬次請求免費
Response Streaming:
- 前 6 MB / 請求:免費
- 超過部分:按 GB 計費,但有每月 100 GiB 的免費額度
CloudFront:
- 資料傳輸:依流量計費
- 新帳戶有每月 1 TB 免費額度(12 個月)
WAF:
- Web ACL:約 $5/月
- 規則:每條約 $1/月
- 請求:$0.60 / 100 萬次
- 基礎成本約 $6-10/月
與 API Gateway 的對比
如果用 API Gateway:
- API Gateway 請求費:$3.50 / 100 萬次(REST API)
- Lambda 執行費:一樣
假設每月 100 萬次請求:
- API Gateway 方案:$3.50(API Gateway)+ Lambda 費用
- Function URL 方案:$6-10(WAF)+ Lambda 費用
看起來 Function URL 方案稍貴,但:
- API Gateway 不支援串流,無法滿足需求
- API Gateway 有 29 秒限制
- Function URL 方案支援到 200 MB 的回應
對於需要邊做邊回傳的串流訊息,Function URL 方案是唯一選擇,而且成本不高,主要的投資其實是在安全性上。
使用 Function URL 的意外好處
在實作過程中,我發現這個架構還有個意外好處:除錯變簡單了。
因為 Function URL 是直接的 HTTPS 端點,我可以用 curl 或 Postman 直接測試,不需要透過 API Gateway 的複雜設定。開發階段可以直接存取 Function URL,上線後再套上 CloudFront。
Function URL 這個 2022 年才出現的功能,簡單解決了串流回應的需求,不過單靠它本身的限制也很明顯,只有配合 CloudFront 和 WAF,剛好能組成一個生產環境可用的架構。
如果你也在做類似的 AI 應用,或是需要處理即時進度更新,這個簡單就能組合上線的搭配是值得考慮一下。