AWS
RabbitMQ
sqs
AmazonMQ

メッセージキューの移行を考えてみる

More than 1 year has passed since last update.

この記事は リクルートライフスタイル Advent Calendar 2017 の14日目の記事です。

はじめに

ホットペッパービューティーでシステム改善を行っている@akechiです。
システム改善の1つとして、RabbitMQの導入を推進しています。

メッセージキューといえば、AWS re:Invent 2017で「Amazon MQ」が発表されました。
Amazon MQの最大の利点は、移行の容易さです。
クラスメソッドさんの記事の通り、エンドポイントを切り替えるだけで移行ができちゃいます。
ただし、これはActiveMQから移行する場合の話です。

では、RabbitMQから移行する場合はどうなるのかを見ていきたいと思います。

RabbitMQでの送受信

まずはじめに、RabbitMQでの送受信の実装を見てみましょう。
一般的な実装方法として、RabbitMQのチュートリアルを参考にします。

RabbitMQSample.java
import com.rabbitmq.client.*;

import java.io.IOException;

public class RabbitMQSample {

    private final static String QUEUE_NAME = "testQueue";

    public static void main(String[] argv) throws Exception {
        // 前処理
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(ホスト名);
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        // メッセージの送信
        String message = "Hello World from RabbitMQ!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));

        // メッセージの受信
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
                    throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("Received '" + message + "'");
            }
        };
        channel.basicConsume(QUEUE_NAME, true, consumer);

        // 後処理
        channel.close();
        connection.close();
    }
}

Amazon MQでの送受信

次に、Amazon MQでの送受信の実装を見てみます。
実装方法はAmazon MQのチュートリアルを参考にします。
送受信の部分に関しては、ほとんど作り変えが必要になりますね。

AmazonMQSample.java
package amazon;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

public class AmazonMQSample {

    private final static String QUEUE_NAME = "testQueue";

    public static void main(String[] args) throws JMSException {

        // 前処理
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(エンドポイント);
        connectionFactory.setUserName(ユーザ名);
        connectionFactory.setPassword(パスワード);
        Connection connection = connectionFactory.createConnection();
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createQueue(QUEUE_NAME);

        // メッセージの送信
        MessageProducer producer = session.createProducer(destination);
        producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
        String text = "Hello World from Amazon MQ!";
        TextMessage producerMessage = session.createTextMessage(text);
        producer.send(producerMessage);

        // メッセージの受信
        MessageConsumer consumer = session.createConsumer(destination);
        Message consumerMessage = consumer.receive(1000);
        TextMessage consumerTextMessage = (TextMessage) consumerMessage;
        System.out.println("Message received: " + consumerTextMessage.getText());

        // 後処理
        producer.close();
        consumer.close();
        session.close();
        connection.close();
    }
}

SQSでの送受信

最後に、SQSでの送受信の実装も見てみましょう。
実装方法はこちらの例を参考にします。
Amazon MQと同様、送受信の部分は作り変えが必要になります。

SQSSample.java
package aws.example.sqs;

import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
import com.amazonaws.services.sqs.model.AmazonSQSException;
import com.amazonaws.services.sqs.model.CreateQueueResult;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.SendMessageBatchRequest;
import com.amazonaws.services.sqs.model.SendMessageBatchRequestEntry;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import java.util.Date;
import java.util.List;

public class SQSSample {

    private final static String QUEUE_NAME = "testQueue";

    public static void main(String[] args) {
        // 前処理
        final AmazonSQS sqs = AmazonSQSClientBuilder.defaultClient();
        String queueUrl = sqs.getQueueUrl(QUEUE_NAME).getQueueUrl();

        // メッセージの送信
        SendMessageRequest send_msg_request = new SendMessageRequest()
                .withQueueUrl(queueUrl)
                .withMessageBody("Hello World from SQS!")
                .withDelaySeconds(5);
        sqs.sendMessage(send_msg_request);

        // メッセージの受信
        List<Message> messages = sqs.receiveMessage(queueUrl).getMessages();
        for (Message m : messages) {
            System.out.println("Message received: " + m.getBody());
            sqs.deleteMessage(queueUrl, m.getReceiptHandle());
        }
    }
}

移行の比較

RabbitMQからの移行コスト(実装)は、Amazon MQとSQSでほとんど同じだと思います。
ただ、移行して得られることはそれぞれ異なります。

移行 得られること
RabbitMQ -> Amazon MQ 運用工数の削減、可用性と耐久性
RabbitMQ -> SQS (上記に加えて)スケーラビリティ

おわりに

現時点では、RabbitMQから移行を考えた場合SQSの方が良いのかなと思います。
ただ、Amazon MQがActiveMQしかサポートしていない今の話で、今後RabbitMQをサポートするとまた話が変わってきますし、十分に考えられる未来だと思っています。

いづれにせよ変わらないことは、最終的にはSQSを使ったシステムに移行していくことがベストだということです。
Amazon MQとSQSの違いは以下のように記載されています。(引用元:https://docs.aws.amazon.com/ja_jp/amazon-mq/latest/developer-guide/welcome.html

Amazon MQ は Amazon SQS や Amazon SNS とはどう違うのですか?

Amazon MQ はマネージド型メッセージブローカーサービスであり、多くの一般的なメッセージブローカーと互換性があります。API (JMS など) や、プロトコル (AMQP、MQTT、OpenWire、Stomp など) との互換性に依存する既存のメッセージブローカーからのアプリケーション移行に Amazon MQ をお勧めします。

Amazon SQS および Amazon SNS はキューおよびトピックサービスであり、高度にスケーラブルでシンプルに使用でき、メッセージブローカーを管理する必要がありません。新規のアプリケーションには Amazon SQS および Amazon SNS をお勧めします。ほぼ無制限の拡張性とシンプルな管理がメリットです。

SQSという強力なメッセージキューサービスを展開しながら、顧客がSQSへの移行に困っていることに課題を感じ、新たにAmazon MQというサービスを提供する姿勢はやはりすごいなと思いました。