SQS / SNSのローカル実装 CMB (Cloud Message Bus) を試してみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

よく訓練されたアップル信者、都元です。Amazon SQS及びSNSは、AWSにおけるスケーラブルでマネージド、そして格安のメッセージングサービスです。これらのプロダクトは「サービス」であり、開発サーバやローカルマシンにインストールして使うものではありません。

しかし、テストや開発目的で、ローカルで動作させたいこともあるかもしれません。ちなみにDynamoDBも同じようなサービスですが、こちらは公式にDynamoDB Local等のソリューションが存在します。

残念ながら、SQSやSNSにはこのような公式の対応がありませんが、GitHubをウロウロしていたら CMB (Cloud Message Bus)という、SQS/SNS互換実装が見つかったので、さっと試してみました。

インストール

CMB本体のインストール

まずはダウンロード。配布ページがあったので、こちらからダウンロードします。本検証では執筆時点の最新版「Version 2.2.43」を利用しました。インストール先はローカルのMac OSXです。

$ cd workdir
$ wget http://cmb-releases.s3-website-us-west-1.amazonaws.com/2.2.43/cmb-distribution-2.2.43.tar.gz
$ tar xzvf cmb-distribution-*.tar.gz

とりあえずはダウンロードして展開するだけ。cmbというディレクトリが掘られ、そこに全てのファイルが展開されます。cmb/config/cmb.propertiesが設定ファイルなので、中を覗いてみましょう。

cmb.cqs.service.url=http://localhost:6059/
cmb.cns.service.url=http://localhost:6061/

これらがローカルでのエンドポイントになります。

#
# configure email relay here, if email protocol is desired for cns, otherwise set enabled to false
#

cmb.cns.smtp.enabled=false
cmb.cns.smtp.hostname=
cmb.cns.smtp.username=
cmb.cns.smtp.password=
cmb.cns.smtp.replyAddress=

また、SNSにおいてemailやemail-jsonのsubscriptionを利用する場合、メールサーバの設定が必要です。残念ながら私の環境でさっと設定できるメールサーバを用意できなかったので、メールの配信までは本稿で検証できていません。設定出来る方はしておきましょう。

他はデフォルトのままで上手く動きました。

CassandraとRedisのインストール

CMBは裏側でCassandraとRedisを利用しています。…本格的じゃないですか。ローカル用の仮実装というイメージからはちょっと離れてしまいますねぇ、というのが本音ですが。まぁGitHub上のrepository descriptionも「A highly available, horizontally scalable queuing and notification service compatible with AWS SQS and SNS」となっていて。高可用性とスケーラビリティを謳ってます。いや、仮実装でいいからもっとライトなの探したほうがいいんすかね。

ま、いいです。とりあえずCassandraとRedisをインストールしましょう。私の環境にはまだインストールされていなかったのでhomebrewでサクッと入れました。

Cassandra

Cassandraはv2系が出ているようですが、ドキュメントに2.0のスキーマ定義がなかったので、1.2を選択。

$ brew install cassandra12
$ brew info cassandra12
cassandra12: stable 1.2.19

$ ln -sfv /usr/local/opt/cassandra12/*.plist ~/Library/LaunchAgents
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.cassandra12.plist

言われるがまま、launchctlの設定も済ませました。

$ cassandra-cli -h localhost -f cmb/schema/cassandra_1.2.schema

そして上記コマンドでスキーマの作成を行います。UUIDっぽいのがズラズラと出てくれば多分成功です。

ちなみに私は起動時にError: Could not find or load main class org.apache.cassandra.cli.CliMainというエラーにぶつかりました。その昔、cassandra-0.7.9をインストールしていたようで、環境変数CASSANDRA_HOMEがv0.7.9を指していました。ご参考に。

そしてもうひとつ。キースペースCNSが見つからん、というエラーでも落ちました。cmb/schema/cassandra_1.2.schemaを覗いてみると、先頭にdrop keyspace文が3つ並んでいます。こうすべきなのかはよくわかりませんが、この3つをコメントアウトしたら上手く動きました。

Redis

$ brew install redis
$ brew info redis
redis: stable 2.8.19 (bottled), HEAD

設定ファイル/usr/local/etc/redis.confを一部編集します。下記の3行を#でコメントアウトしましょう。

   save 900 1
   save 300 10
   save 60 10000

そしてredis-server /usr/local/etc/redis.confコマンドでコンソール起動。

起動と設定

以上で動作環境は整いました。sudo cmb/bin/cmb.shでCMB起動です。sudoが無いと動きませんでした。なぜかは追ってません。

起動したら http://localhost:6059/webui にアクセスしてみてください。

2015-02-17_1228

初期ログインは、username/password共にcns_internalです。

2015-02-17_1228-1

ログインすると、ユーザの管理画面になります。アクセスキーとシークレットが見えますので控えておきましょう。

2015-02-17_1229

表の中のCNS(SNSのCMB実装)をクリックするとこのようにtopic管理画面になりますので、適当にトピック名を入力して作成してみます。

2015-02-17_1229-2

できました。

続いて(メールは届きませんが)subscribeしてみましょう。

2015-02-17_1230

簡単ですね。SNS (CNS)はこのあたりで。引き続き、CQS(SQSのCMB実装)の方も見てみましょう。

2015-02-17_1242

既に5つのキューがありましたが、6つ目を作ってみました。ちなみにこの表は横に長くてですね。。。

2015-02-17_1242-1

AttributeやPurgeにも対応していました。

AWS SDK for Javaから使ってみる

// CNS (SNS) の利用準備
AmazonSNS sns = new AmazonSNSClient(creds);
sns.setEndpoint("http://localhost:6061/");

// CNSにpublish
sns.publish("arn:cmb:cns:eu-west-1:424143290207:cmsample", "Hello, CNS!");

// CQS (SQS) の利用準備
AmazonSQS sqs = new AmazonSQSClient(creds);
sqs.setEndpoint("http://localhost:6059/");

// CQSにsend
SendMessageResult result = sqs.sendMessage("http://localhost:6059/424143290207/cmsamplequeue", "Hello, CQS!");
System.out.println("MessageId = " + result.getMessageId());
System.out.println("MD5OfMessageBody = " + result.getMD5OfMessageBody());
System.out.println("MD5OfMessageAttributes = " + result.getMD5OfMessageAttributes());

// CQSからread
ReceiveMessageResult receiveMessage = sqs.receiveMessage("http://localhost:6059/424143290207/cmsamplequeue");
receiveMessage.getMessages().stream().map(msg -> msg.getBody()).forEach(System.out::println);

なんてことはありませんね。setEndpointで、ローカルを指してやるだけで、動きました。

MessageId = 0:0:0_0_81:2986657831232471042:-5491847274831020032
MD5OfMessageBody = 1b51da5780bbd08ce96081f7b6ba4983
MD5OfMessageAttributes = null
Hello, CQS!

残念ながら、SNSのメッセージの受信確認はできませんでしたが、普通に受信できるものだと思います。

最後に

一応、ドキュメントに明示されていた制限事項を確認しておきましょう。

  • AWS SDK for Java v1.9.14 を使って動作確認をしています。(意外と最新に近いですね)
  • Cassandra 1.0.10 では動きません。1.1.X, 2.0.X, 2.1.XはOK(2.0で動くんじゃないか。。。)
  • CNSはSMSプロトコルはサポートしません。
  • CNSはスロットルポリシーをサポートしません。
  • CNSはモバイルpushをサポートしません。
  • CQNはDLQをサポートしません。

とのことです。この制限の中では便利に使えるのではないでしょうか。