SNS+SQSによるサービス間通信周りのものを簡単に実装してみた

弊社アプリ情報サービスApplivへネイティブ広告を出すため、自社システムApplivAdを運営しています。今まではモノリシックなアーキテクチャで開発され、コード量が増えるにつれアプリケーションが複雑化してきました。

複雑化への対策としてマイクロサービス アーキテクチャへの移行を検討していますが、パフォーマンスが重視されるシステムでは、サービス間通信が大きな懸念点となります。分散コンピューティングの落とし穴はもちろん、サービス間の結合性からもさまざまな問題が生じます。

サービス間通信にRESTのAPIやgRPCなどの同期的なプロトコルが使われることが多いです。しかし、ApplivAdのような計測イベントの多いシステムでは、非同期的なプロトコルをも活かすことで、サービス間の結合性を低めるばかりかリアクティブマニフェストの実現により近づくことも可能になります。

SNS+SQSによる非同期メッセージ

AMQPプロトコル (RabbitMQActiveMQ)、アクターモデル (Akka)、分散ストリーミングプラットフォーム (Kafka) など、非同期メッセージに使えるミドルウェアが数多くあります。弊社ではインフラコストを抑えシステムの柔軟性を高めるため、可能な限りマネージドサービスを利用することにしています。

AWS上のソリューションを少し調べると、SNSのPubSub機能とSQSのメッセージキューを組み合わせている動画をAWSのYouTubeチャンネルで見つけました。複数コンシューマー向けで疎結合性、耐障害性、弾力性といったメリットが魅力的でした。

SNS+SQSによる非同期メッセージ

動画ではアーキテクチャのメリット・デメリットが取り上げられていましたが、本記事では構築と実装に集中したいので以下の項目をカバーします。

  • SNSトピックの作成
  • SQSキューの作成とSNSトピックへのサブスクリプション
  • SNSトピックへのメッセージの発行
  • SQSキューからのSNSメッセージの受信と削除

今回は以下の項目には触れませんので、詳しくはリンク先まで。


トピック、キュー、サブスクリプションのセットアップ

SNSの画面新しいトピックの作成ボタンをクリックし、トピック名を入力してからトピックの作成ボタンでトピックを新しく作成しましょう。(トピックの作成を参照)

SNSトピックの作成

作成したトピックのARNをコピーしておいてから、SQSの画面新しいキューの作成ボタンをクリックし、作成フォームのキュー名を入力してから、デフォルト設定のままでキューの作成ボタンでキューを新しく作成しましょう。(Amazon SQS キューの作成を参照)

SQSキューの作成

作成したキューの右クリックメニューからSNS トピックへのキューのサブスクライブを選択し、先ほどのトピックをドロップダウンリストから選択、またはARNを貼り付けて、サブスクライブボタンでサブスクリプションを作成しましょう。(Amazon SNS トピックへの Amazon SQS キューをサブスクライブするを参照)

SQSキューをSNSトピックにサブスクライブする

最後に作成したキューのURLをコピーしておきましょう。これで実装の準備ができました。


AWS SDKによる実装

これからはAWS SDK for Goを利用し、SNS+SQSの非同期メッセージに必要最低限な実装をしていきます。現時点では残念ながらドキュメントの日本語版はないようです。リファレンス実装のコードはGitHubのレポジトリで確認できます。

まず始めに、AWS SDKに必要な値を以下の環境変数に設定しておきましょう。TOPIC_ARNは先ほど作成したSNSトピックのARNで、QUEUE_URLはSQSキューのURLです。(認証に関してはSpecifying Credentialsを参照)

export AWS_ACCESS_KEY_ID="xxx" export AWS_SECRET_ACCESS_KEY="xxx" export AWS_DEFAULT_REGION="ap-northeast-1"
export TOPIC_ARN="arn:aws:sns:ap-northeast-1:xxx:xxx"
export QUEUE_URL="https://sqs.ap-northeast-1.amazonaws.com/xxx/xxx"

今回の実装にはAWS SDKから以下のパッケージが必要です。インストールはGitHubレポジトリのInstallingセクションから参照、もしくはdepで行いましょう。

SNSとSQSを使う前にはまずAWSのセッションを作成しましょう。認証のキーとシークレットの環境変数はSDKに自動で認識されますが、他はos.Getenvで取得します。

この構築でSNSへメッセージを発行できます。SDKのsnsパッケージを使うと楽です。

エラーが出なかったら、SQSの画面利用可能なメッセージになっていることが確認できます。右クリックメニューのメッセージの表示/削除を選択しポーリングを開始すれば中身も確認できます。

SNSから発行されたメッセージをSQSの中で確認すると、メッセージの本文は指定したTest Messageの文字列ではなく、以下のようなJSONオブジェクトになっていることが分かります。

メッセージをSQSに直接投げるよりデータ量がかなり多くなります。JSONをパースする前にSDKを使ってメッセージを取得してみましょう。今回はsqsパッケージを利用します。WaitTimeSecondsを1秒以上の値に設定することでロングポーリングを利用することができ、受信の効率化やSQSの使用コスト削減に繋がります。

受信が成功した場合は、メッセージが先ほどのようなJSONオブジェクトとして出力されます。このJSONを簡単にパースできるように新しい構造体を定義しておきましょう。

これで標準ライブラリを活かしてJSONを簡単にパースできるようになりました。

最後に再度処理されないようにSQSからメッセージを削除することが必要です。

今までのコードをベースに作った簡易的なプロデューサー/コンシューマーの実装例はGitHubレポジトリで確認できます。


まとめ

実際にSNS+SQSによる非同期メッセージングを利用してみた結果、AWSの管理画面での対応とSDKを使った実装は難なく進められました。セキュリティ、インフラコスト削減、耐障害性などマネージドサービスならではのメリットがあり、CloudWatchを活かすとモニタリングも楽になります。また、新規サービスをリリースしても、既存のプロデューサーを修正せずにサービスのSQSキューをSNSトピックにサブスクライブすれば良いだけです。

ただし、システムの要件によっては本番環境で様々な考慮事項があります。まずはメッセージが重複不可または順序が重要な場合は、FIFOキューの使用が推奨されますが、スタンダートキューより制限が厳しくなり東京リージョンには対応していません。その上、SQSの受信レイテンシーが不安定なので100ミリ秒単位のパフォーマンスが重要なシステムには向いていませんし、SNSからのメッセージはメタデータが多いためデータの通信量がかなり高くなります。


Like what you read? Give James Kirk a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.