Amazon SAA-C03: 應用程式整合、訊息傳遞與串流 — 學習指南
屬於 AWS SAA-C03 — 完整學習指南. 使用經過驗證的解答練習: Amazon 考試中心, 或參加限時模擬考試: ExamRoll.io.
Amazon SQS:解耦、排序與交付語意
Amazon SQS 是一個全託管、拉取式 (pull-based) 的訊息佇列,其主要架構作用是將生產者 (producer) 與消費者 (consumer) 解耦。同步的呼叫鏈會將生產者的延遲和可用性與每個下游相依性綁定;插入一個 SQS 佇列則將其轉換為非同步的交接。生產者根據流量到達的速率將訊息放入佇列,而消費者則以他們能安全處理的速率從佇列取出訊息。這是解決寫入爆量問題的典型方法,這種爆量情況否則會壓垮 RDS 執行個體——佇列會吸收這些爆量,而一個數量有限的消費者叢集會以受控的並行度來消化它,從而控制資料庫的連線數。
佇列有兩種タイプ,這個選擇同時決定了吞吐量和交付保證。
| 功能 | 標準 (Standard) | FIFO |
|---|---|---|
| 排序 | 盡力而為 (Best-effort) | 嚴格,依 MessageGroupId |
| 交付 | 至少一次 (At-least-once) (可能重複) | 僅有一次 (Exactly-once) (在 5 分鐘的重複資料刪除視窗內) |
| 吞吐量 | 近乎無限 | 300 TPS (批次處理為 3,000);啟用高吞吐量模式可達 70,000 |
| 佇列名稱 | 任何名稱 | 必須以 .fifo 結尾 |
標準佇列以至少一次的方式交付,且僅提供盡力而為的排序。當消費者未能在可見性逾時 (visibility timeout) 到期前刪除訊息,或當分散式後端在不同分片 (shard) 之間重播訊息時,就可能出現重複的訊息。即使您在測試中從未見過重複訊息,該服務在架構上仍被允許重新交付,尤其是在代理伺服器容錯移轉期間。假設標準佇列「通常」只會交付一次是一種設計缺陷,而非操作風險——在大規模應用下,重複的訊息最終保證會出現。因此,消費者的邏輯必須是冪等的 (idempotent):在 DynamoDB 中透過條件式寫入來追蹤 MessageId 或業務鍵值、在下游的 API 呼叫中使用冪等鍵 (idempotency key),或依賴更新插入 (upsert) 的語意。
FIFO 佇列在一個 MessageGroupId 內提供嚴格的排序,並透過 MessageDeduplicationId(可以是明確指定,或是訊息本文的 SHA-256 雜湊值)來實現僅有一次的處理,該 ID 會在 5 分鐘的視窗內抑制重複的訊息。MessageGroupId 是關鍵概念:所有共享相同群組 ID 的訊息都會嚴格按照順序,一次只交付給一個消費者,而不同的群組 ID 可以平行處理。對於一個訂單處理系統,若每個客戶的事件必須是循序的,但不同客戶之間是獨立的,就使用 MessageGroupId = customerId。為所有訊息使用單一的群組 ID 會將整個工作負載序列化,並摧毀吞吐量。當使用者重新提交卡住的結帳流程時,重複資料刪除 ID 是防止重複建立訂單的正確基礎元件:客戶端會產生一個確定性的冪等權杖(例如一個與結帳會話綁定的 UUID),而 SQS 會丟棄在該視窗內到達的任何重複提交。
PaymentsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: payments.fifo
FifoQueue: true
ContentBasedDeduplication: true
DeduplicationScope: messageGroup
FifoThroughputLimit: perMessageGroupId
VisibilityTimeout: 60
RedrivePolicy:
deadLetterTargetArn: !GetAtt PaymentsDLQ.Arn
maxReceiveCount: 5
當需求明確指出「需要排序」或「不允許重複」時,選擇標準佇列是典型的錯誤。任何應用程式邏輯都無法恢復佇列從未保留的順序,因為來自不同後端主機的訊息會交錯到達。每當工作負載要求排序(如交易分類帳、狀態機轉換)或僅有一次的語意(如支付請款、庫存扣減)時,都應選擇 FIFO 佇列。
可見性逾時、毒丸訊息與負載限制
當消費者收到一則訊息時,SQS 會在可見性逾時 (visibility timeout) 期間(預設 30 秒,最長 12 小時),使其對其他消費者不可見。如果消費者在逾時到期前刪除訊息,它就消失了;如果沒有——因為消費者崩潰,或處理時間太長——訊息會重新出現並再次被交付。將可見性逾時設定得比實際處理時間短,是導致重複處理的主要原因之一:一個處理需要 45 秒的 Lambda,若其佇列的可見性逾時為預設的 30 秒,則每則訊息都將至少被重複處理兩次。應將逾時時間設定為至少 p99 的處理時間(AWS 對於由 Lambda 驅動的佇列的指導建議是至少設定為函式逾時時間的 6 倍),而對於長度不可預測的工作,則動態地延長逾時時間:
sqs.change_message_visibility(
QueueUrl=queue_url,
ReceiptHandle=handle,
VisibilityTimeout=300 # extend by 5 minutes
)
無效信件佇列 (Dead-letter queues, DLQs) 用於捕獲毒丸訊息 (poison-pill message)。來源佇列上的 RedrivePolicy 會指定一個 maxReceiveCount(通常為 3–5);一旦超過此計數,SQS 會將訊息移至 DLQ 以供離線檢查。DLQ 的類型必須與來源佇列相符 (FIFO ↔ FIFO)。若沒有 DLQ,格式錯誤的訊息會無限循環,這在 FIFO 佇列中尤其具有破壞性——排序機制會阻止同一群組中的後續訊息被交付,直到有問題的訊息被處理為止,因此一則壞訊息會使整個群組停擺。
SQS 訊息的上限為 256 KB。對於更大的負載——例如,一個攜帶已渲染文件的任務——請使用 SQS 延伸用戶端程式庫 (Extended Client Library),它會將負載寫入 S3,並只將一個儲存桶/金鑰參考 (bucket/key reference) 放入佇列。消費者端的程式庫會在接收時自動透明地擷取它。不要將負載分割到多個訊息中(你會失去不可分割性 (atomicity) 和排序性),也不要期望將一個 2 MB 的大型二進位物件 (blob) 做 base64 編碼後就能符合大小限制。
由佇列驅動的 Auto Scaling
對於在 EC2 或 ECS 上、位於 SQS 佇列後方的消費者叢集,正確的擴展訊號不是 CPU——而是佇列的積壓量 (backlog)。CPU 使用率會落後於訊息到達率,並且會將一個飽和的消費者誤讀為「忙碌但還能應付」。典型的擴展指標是 ApproximateNumberOfMessagesVisible,但直接根據原始的佇列深度進行擴展太過粗糙。建議的方法是使用每個執行個體的積壓量 (backlog-per-instance) 這個自訂指標:
backlogPerInstance = ApproximateNumberOfMessagesVisible / RunningInstances
將此指標發佈到 CloudWatch,並在 Auto Scaling 群組或 ECS 服務上驅動一個目標追蹤政策,這樣每個工作單元就能維持一個有上限的積壓量(例如 10 則訊息)。這能在爆量期間實現平滑的向外擴展,並在佇列深度不大但消費者已經飽和時防止擴展活動發生震盪。對於向內縮減 (scale-in),可搭配 ApproximateAgeOfOldestMessage 指標,以避免在仍有舊訊息滯留時終止運算容量。
Amazon SNS:扇出、篩選與跨帳戶交付
SNS 是一種推送式的發布/訂閱 (publish/subscribe) 服務。發布者 (Publisher) 將訊息寫入一個主題 (topic);SNS 會將訊息推送到每一個訂閱:SQS 佇列、Lambda 函式、HTTP(S) 端點、電子郵件、SMS、Kinesis Data Firehose 或行動推播。最主要的持久性模式是 SNS → SQS 扇出 (fan-out):一個主題有多個 SQS 佇列訂閱,如此一來,每個下游服務都有自己的持久性緩衝區、重試政策和 DLQ,而發布者只需要知道該主題即可。如果某個消費者服務中斷數小時,其佇列會累積訊息,並在服務恢復後進行處理——單獨使用 SNS 缺乏這種緩衝能力,且會耗盡其重試政策。
Producer ──▶ SNS topic ──┬──▶ SQS Queue A ──▶ Service A
├──▶ SQS Queue B ──▶ Service B
└──▶ SQS Queue C ──▶ Service C
訊息篩選 (Message filtering) 讓每個訂閱可以宣告一個 JSON 篩選政策,這樣 SNS 就只會交付匹配的訊息,避免了每個消費者都接收所有訊息然後在用戶端 (client-side) 進行篩選的反模式:
{
"eventType": ["order_placed", "order_cancelled"],
"region": ["us-east-1", "us-west-2"]
}
有兩個行為特性很重要。首先,標準 SNS 主題不保證訊息的順序性——每個訂閱者的重試計時器和獨立的網路路徑使得順序重排成為常態。如果順序性很重要,請使用訂閱了 SQS FIFO 佇列 的 SNS FIFO 主題;訊息群組 ID (message group ID) 會在整個流程中傳遞。否則,訂閱者必須是冪等 (idempotent) 的,並且能容忍順序重排。其次,HTTP(S) 訂閱會根據交付政策 (delivery policy) 進行重試(預設為:立即重試三次,然後是指數退避 (exponential backoff) 最長達一小時,之後便會丟棄)。訂閱者必須在 15 秒內回應 2xx、驗證 x-amz-sns-message-type 簽章,並且——對於不可靠的端點——務必設定一個 SNS DLQ (重新驅動至 SQS),這樣未交付的訊息會被捕獲而不是被靜默丟棄。
跨帳戶叫用 (Cross-account invocation) 是一個常見的陷阱。當帳戶 A 發布到一個主題,該主題扇出到帳戶 B 中的一個 Lambda 時,需要兩個政策:SNS 主題政策(或訂閱方向)必須允許該訂閱,且 Lambda 的資源型政策 (resource-based policy) 必須允許來自 sns.amazonaws.com 的 lambda:InvokeFunction,並帶有與該主題匹配的 SourceArn 條件。缺少 Lambda 資源政策是最常見的失敗模式——訂閱看起來正常,但叫用卻被以 403 拒絕。如果主題是使用客戶管理的 KMS 金鑰加密的,則金鑰政策也必須授予發布主體 (principal) 和 sns.amazonaws.com kms:Decrypt 和 kms:GenerateDataKey 的權限。
{
"Effect": "Allow",
"Principal": {"Service": "sns.amazonaws.com"},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:222222222222:function:ProcessOrder",
"Condition": {"ArnLike": {"AWS:SourceArn": "arn:aws:sns:us-east-1:111111111111:orders"}}
}
Amazon EventBridge:路由事件匯流排
EventBridge (前身為 CloudWatch Events) 透過基於內容的路由 (content-based routing)、結構描述探索 (schema discovery)、SaaS 合作夥伴事件來源以及封存/重播 (archive/replay) 功能,擴展了 pub/sub 模型。事件流經事件匯流排 (event buses)(預設、自訂或合作夥伴),並與規則 (rules) 進行匹配,規則的事件模式 (event patterns) 會根據 JSON 結構進行篩選。規則可以透過輸入路徑和輸入範本來轉換酬載、附加無效信件目標 (dead-letter targets),並交付給超過 20 種原生目標,包括 Lambda、Step Functions、ECS 任務、SQS、SNS、Kinesis 和 API 目的地。
{
"source": ["com.acme.orders"],
"detail-type": ["OrderPlaced"],
"detail": {"amount": [{"numeric": [">", 500]}]}
}
與 SNS 的區別在於架構層面。SNS 專為對同質 (homogeneous) 訂閱者進行高吞吐量廣播而優化,具有簡單的屬性篩選和較低的延遲。EventBridge 則專為異質 (heterogeneous) 事件驅動架構而優化:許多生產者發出不同的事件結構描述,而消費者是根據模式而非主題來訂閱。對於一個正在被分解為微服務 (microservices) 的單體式架構 (monolith)——特別是當生產者包括 SaaS 合作夥伴或原生會發出事件的 AWS 服務(如 Config、GuardDuty、CodePipeline、CloudTrail)時——EventBridge 通常是正確的選擇。對於需要對相同訂閱者進行極高流量、低延遲扇出的場景,SNS 仍然勝出,因為 EventBridge 的每事件延遲稍高,且預設的吞吐量上限較低。
Amazon MQ:為現有協定設計的代理訊息傳遞服務
Amazon MQ 是一個執行 ActiveMQ 或 RabbitMQ 的託管代理 (managed broker) 服務。它的存在是為了遷移那些依賴 AMQP 0-9-1、AMQP 1.0、MQTT、STOMP、OpenWire 或 JMS 的本地部署 (on-premises) 工作負載,而無需重寫應用程式。如果一個支付系統使用具有交易性、僅一次語意的第三方 JMS 代理,將其遷移到 Amazon MQ 可以保留線路協定 (wire protocol) 和交付保證,同時免除基礎設施管理。對於全新的 AWS 原生設計,應選擇 SQS/SNS/EventBridge;只有當協定相容性是限制條件時,才選擇 Amazon MQ。
Kinesis Data Streams
Kinesis Data Streams (KDS) 是一種用於高吞吐量串流擷取 (streaming ingest) 的持久、有序、分區的日誌——例如點擊流 (clickstreams)、物聯網遙測 (IoT telemetry)、日誌彙總。記錄會根據 PartitionKey 被放入碎片 (shard) 中;順序性只在單一碎片內得到保證,而非整個串流。每個碎片支援 1 MB/s 或 1,000 筆記錄/秒的寫入,以及 2 MB/s 的讀取(或透過增強型扇出 (Enhanced Fan-Out) 達到更高)。記錄預設保留 24 小時,可延長至 365 天,因此多個獨立的消費者可以重播相同的歷史記錄——這是 SQS 無法做到的,因為 SQS 在確認 (ack) 後會刪除訊息。
隨選模式 (On-demand mode) 透過自動擴展至每個串流最高 200 MiB/s 的寫入量,來免除碎片計算,非常適合流量不可預測的場景。在穩定狀態 (steady state) 且容量已知的情況下,佈建模式 (Provisioned mode) 更便宜。
當工作負載要求有序、可重播的擷取,且吞吐量超過 FIFO 所能應付時(FIFO 的上限遠低於 KDS 能處理的每秒數百萬筆記錄),當多個獨立消費者必須讀取同一個串流時,或者當題目中出現「在整個處理過程中保持原始順序」並伴隨高流量時,應選擇 KDS 而非 SQS FIFO。
Kinesis Data Firehose
Kinesis Data Firehose 是一項全託管的交付服務。它會從 Kinesis stream 或直接 PUT 讀取資料,根據大小 (1–128 MB) 或時間 (60–900 秒,以先達到者為準) 進行緩衝,可選擇性地呼叫 Lambda 進行逐筆記錄的轉換 (例如 PII 清理、格式標準化),可以使用 Glue schema 即時將 JSON 轉換為 Parquet 或 ORC,用 KMS 加密,並交付到 S3、Redshift、OpenSearch 或 Splunk。它沒有分片 (shard),不需執行消費者 (consumer),且採按 GB 計價。
將資料可擴展地擷取到資料湖的典型模式,是將 Data Streams (on-demand) 作為持久性緩衝區,並與 Firehose 配對以交付到 S3:
Producers → Kinesis Data Streams (on-demand) → Firehose (60s buffer, Parquet) → S3 → Athena/Glue
若要擷取數百萬個行動裝置事件、將其加密,並以 Parquet 格式存入 S3,正確的答案是使用具備 Parquet 轉換功能和 KMS 金鑰的 Firehose——而不是 KDS 加上自訂的消費者,再加上手寫的 Parquet writer,後者會需要多出非常多的程式碼和基礎設施。Firehose 是近乎即時的,且不支援消費者端的回放 (replay);當需要回放功能時,應在路徑中保留 KDS。
Kinesis Data Analytics (現為 Managed Service for Apache Flink) 會在串流上執行 SQL 或 Flink 任務,以進行視窗化彙總 (windowed aggregation)。
Lambda 整合與重試語意
Lambda 與這些服務整合時,其重試行為有顯著的不同:
| 來源 | 批次處理 | 順序性 | 失敗時 |
|---|---|---|---|
| SQS Standard | 最多 10,000 則訊息 | 無 | 在可見性逾時後返回佇列;達到 maxReceiveCount 後進入 DLQ |
| SQS FIFO | 依群組 | 依群組 | 群組會被阻擋,直到成功或進入 DLQ |
| Kinesis Streams | 最多 10,000 筆記錄 | 依分片 | 重試會阻擋該分片,直到成功、記錄過期,或送至 MaximumRetryAttempts/OnFailure 目的地 |
| Firehose | 不適用 (轉換用) | 不適用 | 失敗的記錄會存入 S3 的錯誤前綴路徑 |
對於 SQS,請確保 Lambda 函數的逾時時間 ≤ 佇列的可見性逾時,並將可見性逾時設定為至少是函數逾時時間的 6 倍。對於 Kinesis,請啟用 BisectBatchOnFunctionError 並設定一個 OnFailure 目的地 (SQS 或 SNS),這樣單一的毒藥訊息 (poison record) 就不會無限期地卡住整個分片。
選擇決策表
| 需求 | 正確選擇 | 為何其他方案不適用 |
|---|---|---|
| 有序、僅一次的應用程式訊息傳遞,維運量最小 | SQS FIFO | 標準 SQS 缺乏順序性/重複資料刪除;MQ 增加了 broker 管理的負擔 |
| 保留現有的 AMQP/JMS/MQTT 客戶端 | Amazon MQ | SQS/SNS 使用專有 API |
| 將一個事件持久地扇出 (fan-out) 到多個 AWS 消費者 | SNS → 多個 SQS | 生產者與消費者直接耦合會重新導入單體式架構的問題;單獨使用 SNS 在消費者離線時會遺失訊息 |
| 使用篩選/轉換來路由異質事件 | EventBridge | SNS 篩選政策缺乏轉換功能、合作夥伴來源和 schema registry |
| 對相同的訂閱者進行極高吞吐量的扇出 | SNS | EventBridge 的延遲較高,預設吞吐量較低 |
| 擷取並回放高流量的有序串流 | Kinesis Data Streams | SQS 的保留上限為 14 天,且無法依偏移量 (offset) 回放 |
| 無需程式碼即可將串流交付到 S3/Redshift/OpenSearch | Firehose | 單獨使用 Data Streams 需要一個消費者應用程式 |
| 在 S3 中將串流 JSON 轉換為 Parquet | 具備 Glue schema 的 Firehose | 自訂的 KDS 消費者需要撰寫和維運一個 Parquet writer |
← 分析、資料湖、ML 與特殊工作負載 · 所有領域 · 安全性、IAM、KMS 與治理 →
練習這些題目 → · 在 ExamRoll.io 上限時練習 →
Pass the whole exam — not just this question
You found this answer. Get every verified question and explanation in one place, and save hours of prep. Free to start.
通過考試 →