Azure Service Bus に JMS 2.0 API でアクセスする

このエントリは2020/08/12現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容との乖離が発生する可能性があります。

Azure Service Bus(以下、Service Bus)がJMS over AMQPでのJMS 2.0 APIを使った接続をサポートするようになった(2020/08/12現在Preview)。具体的には以下のドキュメントを参照。現時点ではまだ日本語化されていない。

Use Java Message Service 2.0 API with Azure Service Bus Premium (Preview)
https://docs.microsoft.com/azure/service-bus-messaging/how-to-use-java-message-service-20

なお、以下のような似ているドキュメントがあるが、こちらはJMS 1.1仕様のAPIでの接続の場合を説明している。こちらは機能に制限があるので、今後はお勧めできない。

Azure Service Bus と AMQP 1.0 で Java Message Service (JMS) を使用する / Use the Java Message Service (JMS) with Azure Service Bus and AMQP 1.0
https://docs.microsoft.com/azure/service-bus-messaging/service-bus-java-how-to-use-jms-api-amqp

日本語版には記載がないが、英語版では今後はJMS 2.0 APIを使った接続を推奨する、とのメッセージがある。

Warning
The below guide caters to limited support for Java Message Service (JMS) 1.1 API and exists for Azure Service Bus standard tier only.
Full support for the Java Message Service (JMS) 2.0 API is available only on the Azure Service Bus Premium tier in preview, which is highly recommended.

Use the Java Message Service (JMS) with Azure Service Bus and AMQP 1.0
https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-java-how-to-use-jms-api-amqp

どのSKUで使えるのか

Premiumのみ。その他のSKUでは使えない。Standad、Basicなどで使うと、2020/08/12現在以下のような例外が出る(例外メッセージでもPremiumでなければ使えない、と明記している)。

JMSのどの機能がサポートされているのか

ドキュメントに明記されている通り、分散トランザクションはサポートされないので注意が必要。

What JMS features are supported?
https://docs.microsoft.com/azure/service-bus-messaging/how-to-use-java-message-service-20#what-jms-features-are-supported

依存関係

2020/08/12現在では0.0.4が最新。詳細は以下。

azure-servicebus-jms – ServiceBus ConnectionFactory for JMS users
https://search.maven.org/artifact/com.microsoft.azure/azure-servicebus-jms

以下はMavenの例。

<dependency>
  <groupId>com.microsoft.azure</groupId>
  <artifactId>azure-servicebus-jms</artifactId>
  <version>0.0.4</version>
</dependency>

簡単に接続を試す

基本的にドキュメントに記載のとおりやれば問題ない。以下はJMSContextを使う例。

まずは接続ファクトリを作る。

import com.microsoft.azure.servicebus.jms.ServiceBusJmsConnectionFactory;
import com.microsoft.azure.servicebus.jms.ServiceBusJmsConnectionFactorySettings;

...

ServiceBusJmsConnectionFactorySettings connectionFactorySettings = new ServiceBusJmsConnectionFactorySettings();
connectionFactorySettings.setConnectionIdleTimeoutMS(20000);
ConnectionFactory factory = new ServiceBusJmsConnectionFactory("CONNECTION_STRING", connectionFactorySettings);

ここまでできれば、通常のJMS 2.0 APIと同じ。以下はQueueへの送信の例。

try (JMSContext jmsContext = factory.createContext() ) {
    // Create the queue and topic
    Queue queue = jmsContext.createQueue("QUEUE_NAME");
    // Create the JMS message producer
    JMSProducer producer = jmsContext.createProducer();

    // Create textmessage
    TextMessage msg = jmsContext.createTextMessage(String.format("message sent at %s", (new Date()).toString()));
    // send the message to the queue
    producer.send(queue, msg);
}
catch (JMSException e) {
    e.printStackTrace();
}

続いて、Queueからの受信の例。

try (JMSContext jmsContext = factory.createContext() ) {
    // Create the queue and topic
    Queue queue = jmsContext.createQueue(”QUEUE_NAME");
    // set Message Listener
    JMSConsumer consumer = jmsContext.createConsumer(queue);

    // Listener implements MessageListener.
    consumer.setMessageListener(new Listener());

    System.out.println("Receiver is ready, waiting for messages...");
    System.out.println("press Ctrl+c to shutdown...");
    while (true) {
        Thread.sleep(1000);
    }
} catch(InterruptedException e) {
    e.printStackTrace();
}

Message Listenerはこんな感じ。動作確認用途なので非常にシンプルにしている。

public class Listener implements MessageListener {

    public void onMessage(Message m) {
        try {
            TextMessage msg = (TextMessage) m;
            // Show message
            System.out.printf("[Dequeued message at %s] %s\n", (new Date()).toString(), msg.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

Topicのサブスクリプション

通常、Topicからのメッセージを受け取る時は事前にサブスクリプションを作成し、そのサブスクリプションをリスニングするが、まだサブスクリプションが存在しない場合、JMSConsumer オブジェクト作成時に自動的にサブスクリプションを作成する。また、JMSConsumerオブジェクトのクローズ時に作成したサブスクリプションは削除される。この自動生成されるサブスクリプション名は、2020/08/12現在以下のルールに従って作成しているようである(今後の変更の可能性あり)。

1. JMSContext::createConsumerを使う場合

createConsumerを使った場合のサブスクリプション名ルールは以下の通り。

<GUID>$$ND

GUIDを使うのは、createConsumerではサブスクリプション名を指定できないから。NDはNon durableの意味と思われる。Client IDの指定の有無にかかわらず、区切り文字$が2個連続する。以下はその例。

2. JMSContext::createDurableConsumerを使う場合

createDurableConsumerを使った場合のサブスクリプション名ルールは以下の通り。

<コード内で指定したsubscriptionName>$<コード内で指定したclientID>$D

$は区切り文字、最後のDはDurableを明示していると思われる。なお、DurableConsumerではClient IDの指定が必須で、指定しない場合はIllegalStateRuntimeExceptionがスローされる。以下はAzure Portalで見た例(コード内で指定したsubscriptionNameはsubscription1、clientIDはLogico-1)。

3. JMSContext::createSharedConsumerを使う場合

createSharedConsumerを使った場合のサブスクリプション名ルールは以下の通り。

<コード内で指定したサブスクリプション>$<コード内で指定したClient ID>$ND

$は区切り文字、NDはNon durableを意図していると思われる。Client IDを指定しない場合、$$が2個連続する。以下はAzure Portalで見た例。上図はClient IDを指定している場合、下図は指定していない場合(コード内で指定したsubscriptionNameはsubscription1、clientIDはLogico-1)。

4. JMSContext::createSharedDurableConsumerを使う場合

createSharedDurableConsumerを使った場合のサブスクリプション名ルールは以下の通り。

a. Client IDを指定している場合

createDurableConsumerと同じ(<コード内で指定したサブスクリプション>$<コード内で指定したClient ID>$D)。

b. Client IDを指定していない場合

<コード内で指定したサブスクリプション>

以下はClient IDを指定しなかった場合の例(コード内で指定したsubscriptionNameはsubscription1)。

ということで、下表にもまとめたが、コードで指定したsubscriptionNameとAzure Portalに現れるサブスクリプション名と一致するのは、4-bの場合のみ。

メソッド(A) コードで指定したサブスクリプション名(B) Client ID(C) Azure Portalでのサブスクリプション名
createConsumer×<GUID>$$ND
createDurableConsumer<A>$<B>$D
createSharedConsumer<A>$<B>$ND
createSharedDurableConsumerClient ID未指定時:<A>
Client ID指定時:<A>$<B>$D
○は指定必須、△は任意、×は指定不可を表す

Azure Service BusにJMS 2.0 APIでアクセスできると何がうれしいのか

これまで、Javaアプリケーションサーバー上で稼働するオンプレミスアプリケーションをクラウドに移行する場合、Lift-and-Shiftが主流であった。その理由としては

アプリケーションの変更が大変なので…
(PaaSへの移行はやりたくない)

もう塩漬けにするので…
(手を入れたくない)

といったやや後ろ向きのものもあれば、

メッセージングにJMSを多用しているだけでなくアプリケーションサーバーの機能をたくさん使っているため、結果としてIaaSへのLift-and-Shiftを選択せざるを得ない

JMSの分散トランザクションが必須なので…

といったやむを得ないものもあった(本当に分散トランザクションが必要なユースケースはそれほど多くないはずだが、この場ではその議論はしない)。

しかし、例えば以下のような状況があてはまるのであれば、Managed message brokerとしてのAzure Service Busの利活用が選択肢に入る可能性がある。また、JMS 2.0 APIが使えるということで、これまでJava/Jakarta EEを使った開発になれている人にとっては学習に費やす時間が少なくてすむであろう。

  • API呼び出しを使うものの、やはりメッセージングも必要で、順序保証かつ永続化が必須
  • 既存アプリケーションでJMSを使っているが、アプリケーションサーバー固有のJMSの機能は使っていない
  • 既存アプリケーションを最新の開発フレームワーク(Helidon、Quarkus、Micronautなど)ベースに書き換えてモダナイズを予定している。
    • IaaSではなくPaaS(ここでいうPaaSは、アプリケーションをデプロイするだけでよいもの。IaaSに限りなく近いOSレベルの操作ができるものではない)への移行を目指している
    • Kubernetesなどのコンテナオーケストレーションツールを使ったマイクロサービス化を目指している

おまけ

Azure Service BusはPremium SKUであればPrivate Linkをサポートしている。

Private Link
https://azure.microsoft.com/services/private-link/

そのため、仮想ネットワーク(VNet)から外に出ずにAzure Service Busにアクセスできる。つまり、例えば以下のような構成が可能ということ。インターネットからのAzure Service Busへのアクセスは、Firewallで明示的にアクセスを許可していない限りブロックされる。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中