Part 19 of Learn AWS from Scratch

Connecting Services: SQS, SNS and Kinesis

Why services should not call each other directly, how an SQS queue absorbs load, how SNS sends one message to many, when you need a Kinesis stream, and a hands-on where you build a fan-out with a filter.

In this article
  1. Why separate services?
  2. SQS: the queue
  3. SNS: one message to many
  4. Kinesis Data Streams: a stream that stays
  5. Data Firehose: to storage with no code
  6. Which one do I choose?
  7. Amazon MQ: for what already exists
  8. Hands-on: a fan-out with a filter
  9. Summary
  10. Test yourself

So far the pieces of our application call each other directly. That's fine until a sale starts and the invoice service can't keep up, and suddenly the customer is staring at a spinner because of a service they've never heard of.

This section is about modern applications, and it starts with the idea behind most of them: separate the services, so that each one works at its own pace and a failure in one doesn't bring down the others.

Here's a map of this part:

You want Use
A job taken by one worker, with nothing lost SQS
One event reaching many parties at once SNS
A continuous stream that several parties read and replay Kinesis Data Streams
A stream loaded into S3 or similar without writing code Data Firehose
An existing application built on RabbitMQ or ActiveMQ Amazon MQ

Why separate services?

Direct call: the customer waits for the invoice service Store app Invoice service Slow or down With a queue: the customer gets a response right away Store app SQS queue Holds the message Invoice service At its own pace
When services call each other directly, one slow service holds up the customer. A queue lets each side work at its own pace.

A direct call means both sides must be up, and fast, at the same moment. With a queue in between you get three things:

  • Failures stay contained. If the invoice service stops, messages wait in the queue until it comes back.
  • Peaks are absorbed. A thousand orders in a minute don't reach the database all at once.
  • Each side scales alone. You add invoice workers without touching the store.

SQS: the queue

SQS (Simple Queue Service) is a fully managed queue with no servers to run. A producer sends messages, they wait in the queue, and a consumer pulls them, works on them, then deletes them.

Note the direction: consumers ask the queue for messages. The queue doesn't push them to anyone.

Feature Value
Throughput No practical limit on a standard queue
Message size Up to 1 MiB
Retention 4 days by default, from one minute to 14 days
Delivery At least once, so a message can arrive twice
Order Best effort, not guaranteed

The last two points affect your code. Since a message can arrive twice, make processing it twice harmless. An order number you check before charging is enough.

For anything larger than a message, store the file in S3 and send its path in the message.

Visibility timeout

What stops two consumers taking the same message? When one receives it, SQS hides it from the others for a period, called the visibility timeout. The consumer must delete it before that period ends.

Received Hidden from others Processing 30 seconds by default Deleted The job is done Not deleted Appears again
A received message is hidden, not removed. If the consumer doesn't delete it before the timeout ends, it appears again for another consumer.

This is the mechanism that keeps you from losing work: if the server crashes halfway through, the message comes back by itself.

Choose the period to suit your processing time. It's 30 seconds by default, and it can go up to 12 hours:

  • Too short: the message appears again while it's still being worked on, and it gets processed twice.
  • Too long: if a consumer fails, the message waits a long time before anyone retries it.

If a job takes longer than usual, the consumer can extend the period for that message with ChangeMessageVisibility.

Long polling

By default, asking an empty queue returns an empty response right away, so your consumers fill your bill with pointless requests. With long polling, the request waits up to 20 seconds for a message. That means fewer requests, a lower cost and faster delivery, so always turn it on.

Dead-letter queues

What about a message that always fails, because of a bug or bad data? It will come back forever. Attach a dead-letter queue (DLQ): after a number of failed attempts you choose, the message moves there, so you can inspect it without it blocking anything. Then you can redrive it to the original queue once you've fixed the problem.

Keep the retention in the dead-letter queue longer than the original, so messages don't expire before you see them.

FIFO queues

When order matters, as with transactions on one account, use a FIFO queue. Its name must end in .fifo.

Standard FIFO
Order Not guaranteed Guaranteed within a message group
Duplicates Possible Removed within a 5-minute window
Throughput No practical limit 300 messages per second, or 3,000 with batching

The order is per message group, so with one group for each customer, different customers' messages can be processed in parallel. If you need more throughput, high throughput mode raises the limit by far, and the figure varies by Region.

Scaling with the queue

A classic pattern: an Auto Scaling group of workers grows and shrinks according to the queue's length. The ApproximateNumberOfMessagesVisible metric tells you how many messages are waiting, so a growing queue adds workers.

It also protects your database. Instead of a spike of orders hitting RDS directly, they go into a queue, and workers write them at a pace the database can handle. This only suits work that doesn't need an immediate answer, so the customer is told "we received your order", not "your order is complete".

Security

Messages are encrypted in transit over HTTPS, and at rest with SSE-SQS by default, or with your own KMS key. Who can reach the queue is decided by IAM policies, plus an access policy on the queue itself, which you need for other accounts or for services like SNS and S3 to send to it. We'll write one in the hands-on.

SNS: one message to many

A queue suits a job that one party does. But when a customer places an order, the shipping, invoicing and analytics services all want to know. You don't want your application to know about each of them, and to change every time you add one.

SNS (Simple Notification Service) works by publishing and subscribing: your application publishes one message to a topic, and SNS delivers it to every subscriber, right away. A standard topic can have up to 12.5 million subscriptions.

Subscriber type Example
SQS A service that processes at its own pace
Lambda A small function that runs per message
HTTP or HTTPS A webhook in another system
Data Firehose Storing every message in S3
Email, SMS, mobile push Notifying people directly

Unlike a queue, SNS doesn't store messages. If a subscriber is down it retries for a while, but it's not somewhere messages wait.

Message size is 256 KiB by default, and you can raise it on a topic up to 1 MiB, though this limits the topic to 100 subscriptions and to SQS, Lambda and Firehose subscribers only.

Fan-out: SNS with SQS

The most important pattern here combines the two services: SNS spreads the message, and a queue for each service keeps it until that service is ready.

Store app Publishes once SNS topic orders Shipping Physical only Invoices All orders Analytics All orders EC2 Lambda Fargate
The app publishes once, SNS copies the message to every queue, and each service reads from its own queue. The filter sends only physical orders to shipping.

Why this combination?

  • Nothing is lost: if the shipping service stops, its messages wait in its queue.
  • Each service has its own pace, retries and dead-letter queue.
  • Adding a service is easy: a new queue subscribes to the topic, with no change to the publisher.

The same goes for S3 events: S3 doesn't accept two rules with overlapping prefixes for the same event type. So to reach several destinations, send the event to one SNS topic and fan it out from there.

Filtering messages

By default every subscriber receives everything. A filter policy on the subscription lets the shipping queue receive only what concerns it:

JSON
{ "type": ["physical"] }

Messages with type set to digital won't reach it, and you don't pay to process them.

FIFO topics

For order across a fan-out, SNS has FIFO topics, whose names also end in .fifo, with order and duplicate removal like FIFO queues. These are limited to 100 subscriptions per topic, and they suit cases like sending the same sequence of updates to several queues.

Kinesis Data Streams: a stream that stays

Imagine a million sensors sending readings every second, and three parties who want to read them: one for instant alerts, one for dashboards, and one for archiving. A queue doesn't suit this, because a message is gone once it's processed.

Kinesis Data Streams is a stream: records stay in it for the retention period, whether they've been read or not, and each consumer reads from its own position, so it can go back and replay them.

App Sensors Logs Kinesis Data Stream Shard 1 Shard 2 Shard 3 Kept from 24 hours to 365 days Lambda Your app Firehose
Producers write to shards, records with the same key stay in order inside one shard, and several consumers read the same data, each at its own position.
  • Shards: the stream is split into shards. With provisioned capacity, each one takes 1 MB per second of writes and 2 MB per second of reads.
  • Partition key: decides which shard a record goes to. Records with the same key stay in order, so a sensor's number works well as a key.
  • Retention: 24 hours by default, up to 365 days.
  • Record size: 1 MiB by default, and you can raise it up to 10 MiB per stream.

Capacity modes

Mode How it works When
On-demand Starts at 4 MB per second of writes and scales by itself, and you pay for the data Unknown or changing load; start here
Provisioned You set the number of shards and pay for each per hour Steady, known load, which can be cheaper

The ceiling for on-demand depends on the Region: up to 10 GB per second of writes in the largest Regions, and 200 MB per second in others.

Video has its own service: Kinesis Video Streams takes live video from devices such as cameras, stores it, and lets you watch it or process it, live or later.

Data Firehose: to storage with no code

Very often all you want is for the stream to land somewhere. Amazon Data Firehose (formerly Kinesis Data Firehose) takes data, collects it, and delivers it with no code or servers:

  • Sources: your application directly, a Kinesis stream, or others like SNS and CloudWatch Logs.
  • Processing: optionally transforms records with a Lambda function, and can convert them to Parquet.
  • Destinations: S3, Redshift, OpenSearch, Snowflake, Apache Iceberg tables, or an HTTP endpoint.
  • Buffering: it collects data until it reaches a size or a period, such as 5 MiB or 5 minutes for S3, and whichever comes first triggers delivery. So it suits near-real-time loading, not an instant reaction.
Kinesis Data Streams Data Firehose
Purpose A stream your consumers read A pipe that delivers to storage
Consumers You write them The destination is managed for you
Replay Yes, within the retention period No
Timing Immediate Depends on the buffering you set

Which one do I choose?

SQS SNS Kinesis Data Streams
Model Consumers pull Pushes to subscribers Consumers pull
Readers of a message One, then it's deleted Every subscriber Everyone, within the retention period
Stored? Up to 14 days No Up to 365 days
Order With FIFO With FIFO Within a shard
Typical use Background jobs Notifications and fan-out Analytics, logs and streaming data

A quick rule: a job? SQS. An event many want to know about? SNS. A stream of data many read and replay? Kinesis.

There's also EventBridge, which routes events by rules from AWS services and other applications, and we'll cover it in part twenty-five.

Amazon MQ: for what already exists

SQS and SNS are AWS's own services with their own APIs. If you're moving an application built on open protocols such as AMQP, MQTT or STOMP, you won't want to rewrite it. Amazon MQ runs a familiar broker as a managed service, in two engines:

Engine High availability
ActiveMQ Two brokers in two Availability Zones, active and standby, with shared storage on EFS
RabbitMQ A cluster of three nodes across Availability Zones, behind one endpoint

Note that it runs on brokers you choose the size of, so it doesn't scale without limit like SQS and SNS. For a new application, start with those two.

Hands-on: a fan-out with a filter

We'll build the design from the diagram with two queues. From CloudShell, create the topic and the queues:

Bash
TOPIC_ARN=$(aws sns create-topic --name orders \
  --query TopicArn --output text)

SHIP_URL=$(aws sqs create-queue --queue-name shipping \
  --query QueueUrl --output text)
BILL_URL=$(aws sqs create-queue --queue-name billing \
  --query QueueUrl --output text)

SHIP_ARN=$(aws sqs get-queue-attributes --queue-url $SHIP_URL \
  --attribute-names QueueArn --query Attributes.QueueArn --output text)
BILL_ARN=$(aws sqs get-queue-attributes --queue-url $BILL_URL \
  --attribute-names QueueArn --query Attributes.QueueArn --output text)

Queues are private, so allow only this topic to send to them:

Bash
allow_topic() {
  POLICY=$(jq -nc --arg q "$2" --arg t "$TOPIC_ARN" '{
    Version: "2012-10-17",
    Statement: [{
      Effect: "Allow",
      Principal: {Service: "sns.amazonaws.com"},
      Action: "sqs:SendMessage",
      Resource: $q,
      Condition: {ArnEquals: {"aws:SourceArn": $t}}
    }]
  }')
  aws sqs set-queue-attributes --queue-url "$1" \
    --attributes "$(jq -nc --arg p "$POLICY" '{Policy: $p}')"
}

allow_topic $SHIP_URL $SHIP_ARN
allow_topic $BILL_URL $BILL_ARN

Subscribe the queues to the topic: billing gets everything, and shipping gets physical orders only:

Bash
aws sns subscribe --topic-arn $TOPIC_ARN \
  --protocol sqs --notification-endpoint $BILL_ARN \
  --attributes RawMessageDelivery=true

aws sns subscribe --topic-arn $TOPIC_ARN \
  --protocol sqs --notification-endpoint $SHIP_ARN \
  --attributes '{"RawMessageDelivery":"true","FilterPolicy":"{\"type\":[\"physical\"]}"}'

Publish two orders, one physical and one digital:

Bash
aws sns publish --topic-arn $TOPIC_ARN --message "order 1: a book" \
  --message-attributes '{"type":{"DataType":"String","StringValue":"physical"}}'

aws sns publish --topic-arn $TOPIC_ARN --message "order 2: an e-book" \
  --message-attributes '{"type":{"DataType":"String","StringValue":"digital"}}'

Give it a few seconds, then count the messages in each queue:

Bash
for url in $BILL_URL $SHIP_URL; do
  aws sqs get-queue-attributes --queue-url $url \
    --attribute-names ApproximateNumberOfMessages \
    --query Attributes.ApproximateNumberOfMessages --output text
done

You'll see 2 for billing and 1 for shipping, so the filter worked. Now try the visibility timeout. Receive the shipping message, then try again straight away:

Bash
aws sqs receive-message --queue-url $SHIP_URL --wait-time-seconds 5 \
  --query "Messages[0].Body" --output text

aws sqs receive-message --queue-url $SHIP_URL --wait-time-seconds 5 \
  --query "Messages[0].Body" --output text

The first prints order 1: a book, and the second waits five seconds and prints None, because the message is hidden. We didn't delete it, so wait 30 seconds and receive it again, this time deleting it to finish the job:

Bash
HANDLE=$(aws sqs receive-message --queue-url $SHIP_URL --wait-time-seconds 20 \
  --query "Messages[0].ReceiptHandle" --output text)

aws sqs delete-message --queue-url $SHIP_URL --receipt-handle "$HANDLE"

Now the message is gone for good.

Bash
aws sns delete-topic --topic-arn $TOPIC_ARN
aws sqs delete-queue --queue-url $SHIP_URL
aws sqs delete-queue --queue-url $BILL_URL

Summary

  • Separating services contains failures, absorbs peaks and lets each side scale alone.
  • SQS is a queue: a message goes to one consumer, who must delete it before the visibility timeout ends.
  • Always add long polling and a dead-letter queue, and make processing safe to repeat.
  • SNS delivers one message to many, and fan-out with SQS guarantees none of them miss it.
  • Kinesis Data Streams keeps data for several readers to replay, and Firehose delivers it to storage without code.

Test yourself

Before you move on, try these questions to check you've got this part. Each one is a practical scenario, and the explanation appears once you answer.

Question 1 of 6

A ticket site's order page writes straight to RDS. When a big concert goes on sale, the database overloads and orders fail. Customers can accept seeing "we received your order", with the confirmation following a minute later. What absorbs the peak?

Show the answer

Correct answer: B

The queue holds the orders, and workers write them at a pace the database can handle. It suits this case because the customer doesn't need the final answer immediately.

  • A: Replicas serve reads, and the overload here comes from writes.
  • C: SNS delivers right away and doesn't store messages, so the peak would still arrive all at once.
  • D: The standby takes no load, so the overload stays.

Review: Scaling with the queue in this part, and What is Amazon Simple Queue Service? in the AWS docs.

Question 2 of 6

A worker takes about two minutes to produce each invoice, and the queue's visibility timeout is still at its default. Customers are receiving the same invoice several times. What should the team change?

Show the answer

Correct answer: A

After 30 seconds the message reappears while the first worker is still on it, so another worker takes it too. The timeout has to be longer than the work.

  • B: This cuts empty responses, and doesn't change when a message reappears.
  • C: This catches messages that keep failing, and these ones are succeeding, repeatedly.
  • D: More workers would only pick up the reappearing messages faster.

Review: Visibility timeout in this part, and Amazon SQS visibility timeout in the AWS docs.

Question 3 of 6

When an order is placed, the shipping, billing and analytics services all need to know. Any of them may be down for hours, and adding a new service shouldn't mean changing the publisher. What fits?

Show the answer

Correct answer: D

SNS delivers one message to every subscriber, and each service's queue holds it until that service is ready. A new service simply subscribes a new queue.

  • A: Each message goes to only one consumer, so the other services would miss it.
  • B: The publisher would need changing with every new service.
  • C: SNS only retries for a while and doesn't store messages, so a long outage could lose them.

Review: Fan-out: SNS with SQS in this part, and What is Amazon SNS? in the AWS docs.

Question 4 of 6

A wind farm's sensors send readings every second. Three separate applications read the same data in real time, and after fixing a bug, one of them must re-read the last three days. Which option fits?

Show the answer

Correct answer: C

Records stay in the stream for the retention period whether they've been read or not, and each consumer reads from its own position, so one can go back without disturbing the others. The default retention is only 24 hours.

  • A: A message goes to one consumer, and it's gone once deleted.
  • B: SNS doesn't store messages, so there's nothing to re-read.
  • D: This buffers data and delivers it to storage, so it offers neither instant reads nor replay.

Review: Kinesis Data Streams: a stream that stays in this part, and Change the data retention period in the AWS docs.

Question 5 of 6

A bank processes transactions through a queue. Within each account they must run in order with duplicates removed, while different accounts should be processed in parallel. Which two choices fit? (Choose two.)

Show the answer

Correct answer: B + D

A FIFO queue keeps order within a message group and removes duplicates within a 5-minute window, and a group for each account lets different accounts run in parallel.

  • A: This doesn't guarantee order, and a message can arrive twice.
  • C: This forces every transaction into one line, so nothing runs in parallel.
  • E: This gives workers more time, and has no effect on order.

Review: FIFO queues in this part, and FIFO queue delivery logic in Amazon SQS in the AWS docs.

Question 6 of 6

A factory wants to send live video from its security cameras to AWS, store it, and analyze it later. Which service was built for this?

Show the answer

Correct answer: D

It takes live video from devices, stores it, and lets you watch or process it, live or later.

  • A: This delivers records to storage, and isn't built for live video from devices.
  • B: A message holds up to 1 MiB, which doesn't suit video.
  • C: This pushes short messages to subscribers, and stores nothing.

Review: Kinesis Data Streams: a stream that stays in this part, and What is Amazon Kinesis Video Streams? in the AWS docs.

The services are now separated, but where does the code itself run? So far our answer has been an EC2 server, with all the setup that entails. In part twenty we package applications in containers and run them with ECS, Fargate and EKS.