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

この記事は [リクルートライフスタイル Advent Calendar 2017](https://qiita.com/advent-calendar/2017/recruitlifestyle) の14日目の記事です。

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

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

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

# RabbitMQでの送受信
まずはじめに、RabbitMQでの送受信の実装を見てみましょう。
一般的な実装方法として、[RabbitMQのチュートリアル](https://www.rabbitmq.com/tutorials/tutorial-one-java.html)を参考にします。

```java: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のチュートリアル](https://docs.aws.amazon.com/ja_jp/amazon-mq/latest/developer-guide/amazon-mq-connecting-application.html)を参考にします。
送受信の部分に関しては、ほとんど作り変えが必要になりますね。

```java: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での送受信の実装も見てみましょう。
実装方法は[こちらの例](http://docs.aws.amazon.com/ja_jp/sdk-for-java/v1/developer-guide/examples-sqs-messages.html)を参考にします。
Amazon MQと同様、送受信の部分は作り変えが必要になります。

```java: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というサービスを提供する姿勢はやはりすごいなと思いました。