CLOVER🍀

That was when it all began.

Ubuntu LinuxにRabbitMQをインストールする

最近、ずっとキューを扱うミドルウェアをどれか使って遊んでみたいなーと思っていまして、まずは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

利用できる最新バージョンは、このあたりを見ればよいのかな。

http://www.rabbitmq.com/debian/pool/main/r/rabbitmq-server/

起動と停止

「service rabbitmq-server」で行います。

RabbitMQ - rabbitmq-server(8)

起動。

$ 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コマンドを使用するようです。

RabbitMQ - rabbitmqctl(8)

では、作成します。ユーザー名は「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イメージを使ってもよいでしょうけれど、まあ環境構築手順の確認も兼ねてということで。

rabbitmq(Docker Hub)