AWS再入門ブログリレー SQS編

こんにちは、コンサルティング部 もこ@札幌オフィス です!

当エントリは弊社コンサルティング部による『AWS再入門ブログリレー 2019』の22日目のエントリです。

前日は森田 桂介による「DynamoDB」でした。

このブログリレーの企画は、普段AWSサービスについて最新のネタ・深い/細かいテーマを主に書き連ねてきたメンバーの手によって、 今一度初心に返って、基本的な部分を見つめ直してみよう、解説してみようというコンセプトが含まれています。

AWSをこれから学ぼう!という方にとっては文字通りの入門記事として、またすでにAWSを活用されている方にとってもAWSサービスの再発見や2019年のサービスアップデートのキャッチアップの場となればと考えておりますので、ぜひ最後までお付合い頂ければ幸いです。

本日22日目のテーマは『Amazon Simple Queue Service』です。

目次

Amazon Simple Queue Service(SQS)とは?

Amazon Simple Queue Service(以下SQS)はフルマネージド型のメッセージキューサービスです。

「メッセージをSQSの送信」、「SQSからメッセージをポーリング」することができ、SQSを利用することによって

  • 軽い受付処理をした結果をSQSに登録し、別のサーバーからメッセージをポーリングし、重い処理を済ませる

  • SQS側で処理予定のタスクをバッファーでき、スパイク的なアクセスに耐えれる構成を作成できる

  • SQSに登録されているキューに基づいてSQSをポーリングし、処理を行うサーバーをスケールする事ができる

  • SQSをトリガーにLambdaをキックし、サーバーレスが捗る

などなどで、お店で例えて言うと

「注文はチーズバーガー→お会計130円です→チーズバーガーを作成→チーズバーガーを返す」

ような同期的なアプリケーションを

「レジ側: 注文はチーズバーガー→お会計130円です(SQSにキューを登録)」

「厨房側: SQSをポーリング、チーズバーガーを作成、チーズバーガーを返す」

のように、レジと厨房を非同期で処理できるため、レジでは注文と会計のみの仕事に集中もできますし、何よりチーズバーガーを作る作業は厨房に丸投げするため、お客様にも待たさずに次々と注文を受け付けられます。

また、キューの量をトリガーにスケールすることも可能です。

前提知識

メッセージをSQSに送信するアプリケーションのことを「プロデューサー」と呼び、

SQSからメッセージをポーリングするアプリケーションのことを「コンシューマー」と呼びます。

SQSに送信できる最大256KBのメッセージを送信でき、SQS側での最長保持期間は14日です。

SQSのタイプ

SQSには2つの種類があり、「標準キュー」、「FIFOキュー」があります。

標準キュー

「順不同な可能性がある」、「複数回配信される可能背がある」、「ほぼ無制限に送信ができる」、標準的なキューです。

(1, 2, 3, 4)->SQS->(2, 4, 1, 3, 4)

のように順不同で配信される可能性があり、同じメッセージを複数回配信される可能性があります。

複数回同じメッセージを受け取っても悪影響が出ないよう、DynamoDBなどを利用して重複が無いようにする必要があります。

FIFOキュー

「順番配信」、「重複キューの削除」、「一回だけの配信」、「1秒に送信できるキューに制限がある」キューです。

つまり、

(1, 2, 3, 4)->SQS->(1, 2, 3, 4)

となり、DynamoDBなどを利用して重複処理を回避する必要もなくなります!

FIFOキューの制限

SendMessage、ReceiveMessage、DeleteMessageは 300 Message/s

SendMessageBatch、DeleteMessageBatch、ChangeMessageVisibilityBatchを利用すると最大 3000 Message/sとなります。

(SendMessageBatchなどのBatch系では最大10件のメッセージを一つのリクエストで送信することができます)

また、FIFOキューではLambda, SNSなどの連携をサポートしていないため、利用する場合は標準キューを利用する必要があります。

SQSのライフサイクル

SQSには主に「可視性タイムアウト」、「メッセージ保持期間」の設定があります。

可視性タイムアウトは、「コンシューマーがメッセージを受信してから可視性タイムアウトの間、他のコンシューマーなどにキューを配信しない」ものです。

メッセージ保持期間は「SQSにメッセージ保持する期間」で、SQSに登録されてから処理されずにSQSに溜まったメッセージが削除されるまでの期間です。

SQSを使ってアプリケーションを作ってみた

ここまでSQSについて紹介しましたが、実際に使ってみないとよくわからないと思います。

実際にSQSを利用し、「ハンバーガーの注文を受け、厨房で作成する」ものを作ってみたいと思います。

(レジ) ハンバーガーの注文を受け付ける

//@ts-check
const AWS = require("aws-sdk");
const SQS = new AWS.SQS({region: "ap-northeast-1"})

const QueueUrl = "https://sqs.ap-northeast-1.amazonaws.com/xxxxxxxxxxxx/queue";

function order(burger, amount) {
  const MessageBody = JSON.stringify({burger, amount})
  SQS.sendMessage({
    MessageBody,
    QueueUrl,
  }, (err, data) => {
    if(err) return console.error(err, err.stack);
    console.log(data)
  });

}

order("チーズバーガー");

(厨房) ハンバーガーの注文を処理する

//@ts-check
const AWS = require("aws-sdk");
const sqs = new AWS.SQS({ region: "ap-northeast-1" });

const QueueUrl = "https://sqs.ap-northeast-1.amazonaws.com/xxxxxxxxxxxx/queue";

const receiveMessage = QueueUrl =>
  new Promise((resolve, reject) => {
    const params = {
      QueueUrl,
      MaxNumberOfMessages: 10,
      WaitTimeSeconds: 10
    };
    sqs.receiveMessage(params, (err, data) => {
      if (err || !data.Messages) return reject(err);
      return resolve(data);
    });
  });

const deleteMessage = (ReceiptHandle, QueueUrl) => {
  const params = {
    QueueUrl,
    ReceiptHandle
  };
  sqs.deleteMessage(params, (err, data) => {
    if (err) return console.error(err);
    console.log("delete queue");
    console.log(data);
  });
};

const createBurger = burger =>
  new Promise(resolve => {
    setTimeout(() => resolve("🍔"), 0);
  });

async function main() {
  const messageids = [];

  while (true) {
    const data = await receiveMessage(QueueUrl).catch(console.error);
    if (!data) continue;
    for (const message of data.Messages) {
      // すでに処理済みだったらスキップする
      // DynamoDBなどを利用するといい感じにできます。
      if (messageids.includes(message.MessageId)) return;

      // ハンバーガーを作る
      const order = JSON.parse(message.Body);

      // 重い処理
      console.log(order.burger)
      const result = await createBurger(order.burger);
      console.log(result)
      messageids.push(message.MessageId);

      deleteMessage(message.ReceiptHandle, QueueUrl);
    }
  }
}

main();

標準SQSを利用しているため、実際に利用する場合はDynamoDBを使うなどで受信側で重複を回避する必要があるので、注意が必要です。

まとめ

このように、SQSを利用すると簡単にサービスを分離することができ、AWS側にキューの処理などをまるまる任せられるので、とても便利です!

今回の「AWS再入門ブログリレー」もおかげさまで非常に皆様の高評価を戴いた企画となりました。ご支持戴き誠にありがとうございました!

本リレーを通してAWSのたくさんある様々なサービスに再入門できたなら幸いです!