最近、ずっとキューを扱うミドルウェアをどれか使って遊んでみたいなーと思っていまして、まずはRabbitMQで遊んでみることにしました。
RabbitMQ - Messaging that just works
ドキュメントは、こちら。
RabbitMQ - Documentation: Table of Contents
とりあえずキューを使ってみたいというくらいの軽いノリなので、RabbitMQのここがいい!みたいな感覚は全然持っていないのですが…このあたりから試してみたらいいかなという、そのくらいの気分で始めています。クラスタも組めるっぽいですし。
チュートリアルからなぞっていって、気が済むまで遊んでみようと思います。
参考までに、ちょっと読んでいたページ。
分散型メッセージングミドルウェアの詳細比較 | POSTD
Ubuntu LinuxにRabbitMQをインストールする
各プラットフォームに対して、インストールのガイドがあるので、こちらを参考にUbuntu LinuxにRabbitMQをインストールしてみます。
RabbitMQ - Downloading and Installing RabbitMQ
RabbitMQ - Installing on Debian and Ubuntu
最初に、リポジトリの設定をします。
$ echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list $ wget -O- https://www.rabbitmq.com/rabbitmq-signing-key-public.asc | sudo apt-key add - $ sudo apt-get update
あとは、apt-getでインストールするだけ。
$ sudo apt-get install rabbitmq-server
バージョンを指定してインストールする場合は、このように。
$ sudo apt-get install rabbitmq-server=3.6.2-1
利用できる最新バージョンは、このあたりを見ればよいのかな。
起動と停止
「service rabbitmq-server」で行います。
起動。
$ sudo service rabbitmq-server start
* Starting message broker rabbitmq-server
停止。
$ sudo service rabbitmq-server stop
* Stopping message broker rabbitmq-server
ログは、「/var/log/rabbitmq」に出力されるようです。
$ ls -l /var/log/rabbitmq total 8 -rw-r--r-- 1 rabbitmq rabbitmq 0 May 31 14:20 rabbit@my-rabbitmq-server-sasl.log -rw-r--r-- 1 rabbitmq rabbitmq 2048 May 31 14:20 rabbit@my-rabbitmq-server.log -rw-r--r-- 1 rabbitmq rabbitmq 0 May 31 14:20 startup_err -rw-r--r-- 1 rabbitmq rabbitmq 369 May 31 14:20 startup_log
データは、「/var/lib/rabbitmq/mnesia」にできるようです。
$ ls -l /var/lib/rabbitmq/mnesia total 8 drwxr-xr-x 4 rabbitmq rabbitmq 4096 May 31 14:20 rabbit@my-rabbitmq-server drwxr-xr-x 2 rabbitmq rabbitmq 4096 May 31 14:20 rabbit@my-rabbitmq-server-plugins-expand
設定ファイルは、「/etc/rabbitmq」に置くものみたいです。インストール直後はありませんけど。
$ ls -l /etc/rabbitmq total 0
動作確認
インストールしたので、動作確認してみましょう。
まずはHello Worldということで。
RabbitMQ - RabbitMQ tutorial - "Hello World!"
キューを作って、メッセージを送信、受信するサンプルのようです。
今回は、Javaクライアントを使って書いてみましょう。
Maven依存関係に、以下を定義します。JUnitとAssertJは、テストコード用です。
<dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>3.6.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.4.1</version> <scope>test</scope> </dependency>
で、書いたコードはこんな感じ。
src/test/java/org/littlewings/rabbimq/helloworld/RabbitMqHelloWorldTest.java
package org.littlewings.rabbimq.helloworld; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.Envelope; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class RabbitMqHelloWorldTest { @Test public void helloWorld() throws IOException, TimeoutException { send(); receive(); } private void send() throws IOException, TimeoutException { // 送信側コード } private void receive() throws IOException, TimeoutException { // 受信側コード } }
送信、受信のコードを続けて書いていきます。
受信側から。
private void send() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setPort(5672); factory.setUsername("kazuhira"); factory.setPassword("password"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare("my-queue", false, false, false, null); channel.basicPublish("", "my-queue", null, "Hello RabbitMQ!!".getBytes(StandardCharsets.UTF_8)); channel.close(); connection.close(); }
ConnectionFactoryで接続先やユーザーを設定して、Connection、そしてChannelを得る感じで書いていくようです。
そして、Channel#queueDeclareでキューを作成します。今回は、「my-queue」という名前のキューを作成しました。
ところで、RabbitMQはlocalhostにつなぐのならguestアカウントが使えるそうです。が、実はRabbitMQをDockerで動かしていたのでリモート接続扱いになり、ユーザーを作成しなくてはならなくなりました…。
Username/Passwordを指定していなかった場合で、かつリモート接続の場合はこのようなエラーに遭遇します。
com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
ログには、こんなエラーが出ています…。
=ERROR REPORT==== 31-May-2016::14:41:47 === Error on AMQP connection <0.490.0> (172.17.0.1:44495 -> 172.17.0.2:5672, state: starting): PLAIN login refused: user 'guest' can only connect via localhost
仕方がないので、ユーザーを作成しましょう。今回は、VirtualHost「/」に対してユーザーを作成します。
ユーザーの作成には、rabbitmqctlコマンドを使用するようです。
では、作成します。ユーザー名は「kazuhira」、パスワードは「password」とします。
$ sudo -u rabbitmq rabbitmqctl add_user kazuhira password Creating user "kazuhira" ... $ sudo -u rabbitmq rabbitmqctl set_permissions -p / kazuhira ".*" ".*" ".*" Setting permissions for user "kazuhira" in vhost "/" ...
これで、認証できるようになります。
そして受信側。
private void receive() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setPort(5672); factory.setUsername("kazuhira"); factory.setPassword("password"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare("my-queue", false, false, false, null); AtomicInteger counter = new AtomicInteger(); channel.basicConsume("my-queue", true, new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) { assertThat(envelope.getRoutingKey()).isEqualTo("my-queue"); assertThat(new String(body, StandardCharsets.UTF_8)).isEqualTo("Hello RabbitMQ!!"); counter.incrementAndGet(); } }); try { TimeUnit.SECONDS.sleep(1L); } catch (InterruptedException e) { } assertThat(counter.get()).isEqualTo(1); channel.close(); connection.close(); }
キューを作成するところまでは同じで(受信側で作っているので、こちらではChannel#queueDeclareしなくても動きましたが、送信と受信のどちらが先に起動するかわからないケースもあるので、それぞれで宣言した方がよいみたいです)、あとはConsumerをchannelに登録します。Consumer登録時には、やっぱりキューの名前(「my-queue」)を指定します。
とりあえず、これでHello Worldは達成できました。
まとめ
はじめてのRabbitMQということで、ちょっと試してみました。
若干認証のところでハマりましたが、それさえクリアすればクライアント側は動かすこと自体は簡単にできました。これから、少しずつ試していってみようと思います。
オマケ
先にちょっと書いてしまいましたが、Dockerfileも作ったのでこちらも貼っておきます。
Dockerfile
FROM ubuntu:latest # see: http://www.rabbitmq.com/debian/pool/main/r/rabbitmq-server/ ENV RABBITMQ_VERSION 3.6.2-1 EXPOSE 5672 25672 RUN apt-get update && \ apt-get install -y wget \ curl \ vim RUN echo 'deb http://www.rabbitmq.com/debian/ testing main' | \ tee /etc/apt/sources.list.d/rabbitmq.list && \ wget -O- https://www.rabbitmq.com/rabbitmq-signing-key-public.asc | \ apt-key add - RUN apt-get update && \ apt-get install -y rabbitmq-server=${RABBITMQ_VERSION} ENTRYPOINT service rabbitmq-server start && tail -f /dev/null
オフィシャルのDockerイメージを使ってもよいでしょうけれど、まあ環境構築手順の確認も兼ねてということで。