ActiveMQからAmazon MQへの移行はどれほど簡単なのか #reinvent

2017.12.11

こんにちは。サービスグループの武田です。

re:Invent 2017も盛況のうちに終わり、弊社もまた日常が戻ってきました。

今回の発表もさまざまなものがありましたが、その中にActiveMQのフルマネージドサービスがありました。その名もAmazon MQ。さっそくいくつか記事も上がっていましたね。

【速報】新サービスAmazon MQを早速使ってみた! #reinvent

Amazon MQによるエンタープライズシステムのクラウドマイグレーション #reinvent

AWSにはもともとAmazon SQSというキューイングサービスや、Amazon SNSというトピックサービスがありました。ユースケースとしては、Amazon MQは既存の環境からの移行、SQSやSNSは新規のアプリケーション開発に使うとよいようです。

今回は既存のActiveMQを使用しているアプリケーションが、どれほど簡単にAmazon MQに移行できるのか気になったので試してみました。

環境

$ docker --version
Docker version 17.09.0-ce, build afdb6d4

$ gradle --version

------------------------------------------------------------
Gradle 4.4
------------------------------------------------------------

Build time: 2017-12-06 09:05:06 UTC
Revision: cf7821a6f79f8e2a598df21780e3ff7ce8db2b82

Groovy: 2.4.12
Ant: Apache Ant(TM) version 1.9.9 compiled on February 2 2017
JVM: 1.8.0_144 (Oracle Corporation 25.144-b01)
OS: Mac OS X 10.12.6 x86_64

ActiveMQの準備

まずは移行前の環境をシミュレートするために、ローカルにActiveMQの環境を用意します。

今回はDocker Composeでさくっと用意しました。

$ mkdir /path/to/activemq && cd $_

$ vim docker-compose.yml

$ docker-compose up -d

docker-compose.yml

activemq:
  image: webcenter/activemq:latest
  ports:
    - 8161:8161
    - 61616:61616
    - 61613:61613
  environment:
    ACTIVEMQ_NAME: amq
    ACTIVEMQ_REMOVE_DEFAULT_ACCOUNT: 'True'
    ACTIVEMQ_ADMIN_LOGIN: admin
    ACTIVEMQ_ADMIN_PASSWORD: admin
  volumes:
    - ./activemq/data:/data/activemq
    - ./activemq/log:/var/log/activemq

起動できたらブラウザから管理コンソールにアクセスしてみます。URLはhttp://localhost:8161/となっています。

Gradleを実行する

GradleでJavaを実行するための準備と、ActiveMQへの接続に必要なライブラリの依存関係も定義します。

$ mkdir java cd $_
$ mkdir -p src/main/{java/jp/cm/example,resources}

$ vim build.gradle
$ vim src/main/java/jp/cm/example/Main.java

build.gradle

apply plugin: 'application'

mainClassName = 'jp.cm.example.Main'

repositories {
    mavenCentral()
}

ext {
    slf4jVersion = '1.7.25'
    log4jVersion = '2.10.0'

    activemqVersion = '5.15.2'
}

dependencies {
    compile "org.slf4j:slf4j-log4j12:$slf4jVersion"
    compile "org.apache.logging.log4j:log4j-core:$log4jVersion"

    compile "org.apache.activemq:activemq-client:$activemqVersion"
}

src/main/java/jp/cm/example/Main.java

package jp.cm.example;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Amazon MQ!");
    }
}

Main.javaはとりあえず動作確認用に、テキストを出力するだけです。 ファイルを用意できたらタスクを実行してみます。

$ gradle run

> Task :run
Hello, Amazon MQ!

BUILD SUCCESSFUL in 8s
2 actionable tasks: 2 executed

JavaからActiveMQに接続

次にJNDIとLog4jのプロパティファイルを作成します。

$ vim src/main/resources/jndi.properties
$ vim src/main/resources/log4j.properties

src/main/resources/jndi.properties

java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url = tcp://localhost:61616
queue.DevQueue = queue-sample-dev

src/main/resources/log4j.properties

log4j.rootLogger=INFO, ROOT
log4j.appender.ROOT=org.apache.log4j.ConsoleAppender
log4j.appender.ROOT.layout=org.apache.log4j.PatternLayout
log4j.appender.ROOT.layout.ConversionPattern=%-5p - %m%n
log4j.appender.null=org.apache.log4j.varia.NullAppender

いよいよ先ほど作成したMain.javaを書き換え、ActiveMQに接続してみましょう。

プログラムはいたってシンプルで、キューにメッセージを送信するだけです。実行は先ほどと同じコマンドです。

$ vim src/main/java/jp/cm/example/Main.java

src/main/java/jp/cm/example/Main.java

package jp.cm.example;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;

public class Main {
    private static final String QUEUE_NAME = "DevQueue";
    private static final String MESSAGE_BODY = "Test Message";

    public static void main(String[] args) throws Exception {
        Context context = new InitialContext();
        System.out.println(context.getEnvironment());

        ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup("ConnectionFactory");
        Connection connection = connectionFactory.createConnection();
        connection.start();

        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination = (Destination) context.lookup(QUEUE_NAME);

        MessageProducer producer = session.createProducer(destination);
        producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

        TextMessage message = session.createTextMessage(MESSAGE_BODY);
        producer.send(message);
        System.out.println("Send message id: " + message.getJMSMessageID());

        session.close();
        connection.close();
    }
}
$ gradle run

> Task :run
{java.naming.provider.url=tcp://localhost:61616, java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory, queue.DevQueue=queue-sample-dev}
Send message id: ID:HL00235-58633-1512844370717-1:1:1:1:1

BUILD SUCCESSFUL in 1s
3 actionable tasks: 2 executed, 1 up-to-date

ブラウザから結果を確認してみます。

docker-compose.ymlで指定しているユーザー名とパスワードを指定します。

たしかにキューに入ってますね!

Amazon MQでブローカーを作成

Amazon MQはまだ東京リージョンに来ていないため、今回はバージニア北部で作成しました。

また8162番ポートと61617番ポートにアクセスできるよう、セキュリティグループを設定する必要があります。

すべてデフォルトで作成した後にセキュリティグループをアタッチしようとしたんですが、ブローカー作成後にアタッチする方法がわかりませんでした 。先にセキュリティグループを作成しておき、作成時にアタッチするのが無難そうです。

スクリーンショットの時間を見ると、約13分ほどかかっていました。

ブローカー名を選択すると、詳細画面に移ります。

OpenWireのエンドポイントはこのあと使うため、メモしておきましょう。

ブラウザからhttps://b-0f872849-a819-48cf-8155-9c4879bdf35a-1.mq.us-east-1.amazonaws.com:8162/にアクセスするとAmazon MQの管理コンソールにアクセスできます。

JavaからAmazon MQに接続

接続先の切り替えは、jndi.propertiesを書き換えるだけです。ただし、先ほどのローカルと異なり、ユーザー名とパスワードによる認証が必要です。java.naming.provider.urlは作成したブローカーの、OpenWireのエンドポイント。userNamepasswordはブローカーを作成するときに指定したものとしてください。

$ cp src/main/resources/jndi.properties{,.local}
$ vim src/main/resources/jndi.properties

src/main/resources/jndi.properties

java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url = ssl://b-0f872849-a819-48cf-8155-9c4879bdf35a-1.mq.us-east-1.amazonaws.com:61617
queue.DevQueue = queue-sample-dev
userName = takeda.takashi
password = ************

実行方法は変わらず、gradle runを実行します。

$ gradle run
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :run
{java.naming.provider.url=ssl://b-0f872849-a819-48cf-8155-9c4879bdf35a-1.mq.us-east-1.amazonaws.com:61617, java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory, password=************, queue.DevQueue=queue-sample-dev, userName=takeda.takashi}
Send message id: ID:HL00235-59715-1512848178473-1:1:1:1:1

BUILD SUCCESSFUL in 6s
3 actionable tasks: 2 executed, 1 up-to-date

ブラウザから結果を確認してみます。

管理コンソールの操作方法は先ほどと同じです。

キューに入っていることが確認できました!

まとめ

非常にシンプルなプログラムでしたが、JNDIのプロバイダを切り替えるだけで、既存のプログラムからAmazon MQへの接続が確認できました。

今回はプロデューサーのプログラムのみでしたが、コンシューマーも同じように切り替えることが可能です。

SQSやSNSへの移行は見送っていた方も、Amazon MQへの移行を検討してみてはいかがでしょうか。

合わせて読みたい