読者です 読者をやめる 読者になる 読者になる

kagamihogeの日記

kagamihogeの日記です。

The Java EE 7 TutorialのJava Message Service Conceptsの章をテキトーに訳した・1

The Java EE 7 Tutorial45 Java Message Service Conceptsのセクションを読んで訳した。

英語(とJava)の勉強が主な目的なんで、ロクに推敲してないです。なんか変なとこがあったら原文を参照して頂きたく。

Messaging

Part IXではメッセージングの解説を行います。このパートには以下のチャプターが含まれます。

45 Java Message Service Concepts

このチャプターではJava Message Service (JMS) APIの紹介を行い、このJava APIによってアプリケーションでは、信頼性のあるメッセージの読み込み・生成・送受信・非同期・疎結合の通信が可能になります。以下のトピックを扱います。

45.1 Overview of the JMS API

この概要では、メッセージングの概念の定義、JMS APIの説明とそれを使う場所、Java EEプラットフォーム内でJMS APIをどのように動かすかについての説明、を行います。

45.1.1 What Is Messaging?

メッセージングとはソフトウェアコンポーネントやアプリケーション間の通信方式のことです。メッセージングシステムはP2Pの形態を取り、メッセージングクライアントはメッセージ送信が可能で、また、他の任意のクライアントからメッセージを受信可能です。各クライアントはメッセージの読込・生成・送信・受信の機能を提供するメッセージングエージェントに接続します。

メッセージングは疎結合(loosely coupled)の分散通信を可能にします。コンポーネントdestination*1にメッセージを送信し、受信側はdestinationからメッセージを取得します。疎結合の通信を可能にしているのは、送信者と受信者が共通のdestinationを持つ点です。送信者と受信者を通信時に同時に使うことは出来ません。実際には、送信者は受信者について特に知る必要はなく、同様に、受信者も送信者について知る必要はありません。送信者と受信者が知る必要があるのはメッセージフォーマットとdestinationだけです。この点において、メッセージングは密結合のテクノロジとは異なり、例えばRemote Method Invocation (RMI)はアプリケーションがリモートアプリケーションのメソッドを知っていることを要求します。

また、メッセージングは電子メールとも異なり、こちらは人対人もしくはソフトウェアアプリケーション対人の通信方式です。メッセージングはソフトウェアアプリケーションやソフトウェアコンポーネント間の通信に使うものです。

45.1.2 What Is the JMS API?

The Java Message ServiceはJava APIで、アプリケーションでメッセージの読込・生成・送信・受信を行えます。JMS APIはインタフェースの共通セットとJavaのプログラムが他のメッセージング実装と通信するための関連セマンティクスを定義します。

JMS APIは、プログラムが知る必要のあるメッセージングプロダクトの使用方法に関する概念の範囲を最小限度に抑えてはいますが、高機能なメッセージングアプリケーションをサポートするのに十分な機能を備えています。また、JMSプロバイダを介するJMSアプリケーションのポータビリティを最大化するよう努力しています。

JMSは疎結合というわけではない通信もサポートしており、

  • 非同期(Asynchronous):受信クライアントはクライアントが送信するのと同時にメッセージ受信しなくても構いません。送信クライアントはメッセージ送信後に別タスクに移れます。受信クライアントは後々に受信が可能です。
  • Reliable:JMS APIを実装するメッセージングプロバイダはメッセージがただ一度だけ送信されたことを保証可能です。低信頼性でのアプリケーションはメッセージ消失やメッセージ重複受信を許容します。

JMS仕様の現在バージョンはVersion 2.0です。仕様はJava Community Processウェブサイト http://www.jcp.org/en/jsr/detail?id=343 からダウンロード可能です。

45.1.3 When Can You Use the JMS API?

エンタープライズアプリケーションプロバイダは、以下のような状況ではRPCなどの密結合APIよりも、メッセージングAPIを好む傾向にあります。

  • あるコンポーネントを、容易に置換可能にするため、他コンポーネントのインタフェースに関する情報に依存させたくない。
  • すべてのコンポーネントを同時に起動および動作しているかに関わらず、アプリケーションを起動したい。
  • アプリケーションのビジネスモデルが、即時レスポンスを受け取ることなく、操作を継続したり、コンポーネントが別のコンポーネントに情報を送信出来たりする。

たとえば、自動車メーカーのエンタープライズアプリケーションのコンポーネントは以下のような状況でJMS APIを使えます。

  • 在庫コンポーネントは、商品の在庫レベルがあるレベルを下回った場合に工場へ生産依頼をするため、工場コンポーネントにメッセージを送信します。
  • 工場コンポーネントは必要な部品を集めるために部品コンポーネントにメッセージを送信します。
  • 部品コンポーネントは順次、自身に関連する在庫にメッセージを送信し、在庫を更新する指示を出し、サプライヤに新しい部品の注文をします。
  • 工場と部品コンポーネントは会計コンポーネントに予算数値の更新をするためのメッセージを送信します。
  • 販売部隊に更新したカタログを配ります。

これらのタスクにメッセージングを使うことで各種のコンポーネントが効果的に相互通信可能になり、ネットワークやその他リソースと強く結びつく必要はありません。Figure 45-1はこの単純な例を図示したものです。

Figure 45-1 Messaging in an Enterprise Application

Description of "Figure 45-1 Messaging in an Enterprise Application"

Description of "Figure 45-1 Messaging in an Enterprise Application"

製造業はJSM APIを使用可能なエンタープライズの一例に過ぎません。小売・金融サービス・ヘルスケア、その他多くのアプリケーションでメッセージングを使用可能です。

45.1.4 How Does the JMS API Work with the Java EE Platform?

JMS APIの導入当初、最も重要な目的はJavaアプリケーションを既存のメッセージング指向ミドルウェア(messaging-oriented middleware:MOM)にアクセス可能にすることでした。それ以降、多くのベンダーがJMS APIを採用して実装した結果、現在では、JMSプロダクトはエンタープライズ用の完全なメッセージング機能を提供可能になりました。

JMS APIJava EEプラットフォームにとって不可欠な機能であり、アプリケーション開発者はJava EEコンポーネントとメッセージングを組み合わせることが可能です。JSM 2.0はJava EE 7リリースに含まれます。

Java EEプラットフォームのJMS APIは以下の機能を持ちます。

  • アプリケーションクライアント・EJBコンポーネント・webコンポーネントはJMSメッセージの送信と同期受信が可能です。加えて、アプリケーションクライアントはメッセージが利用可能になった時点で通知を受け取ることで非同期にJMSメッセージを受信可能なメッセージリスナーを設定できます。
  • EJBの一種であるメッセージドリブンビーンはEJBコンテナ上で非同期のメッセージ受信が可能です。一般的にアプリケーションサーバはメッセージのコンカレント処理を実装するのにメッセージドリブンビーンをプールしています。
  • メッセージの送受信操作はJava Transaction API (JTA)のトランザクションに参加可能で、JMS操作とDBアクセスを単一のトランザクション内で扱うことが出来ます。

JMS APIは、Java EEコンポーネントとメッセージング機能を持つレガシーシステム間に、非同期通信・信頼性・疎結合エンタープライズ開発の簡素化を設けることで、Java EEプラットフォームの他の機能を強化しています。固有のビジネスイベントを操作する新規のメッセージドリブンビーンを追加することで、開発者は既存のビジネスイベントを持つJava EEアプリケーションに新しい振る舞いを容易に追加できます。また、Java EEプラットフォームは非同期メッセージ受信とJTAトランザクションサポートの提供によりJMS APIを機能強化しています。詳細については、EJB仕様v3.2を参照してください。

JMSプロバイダはJava EE Connectorアーキテクチャを使用するアプリケーションサーバに統合可能です。リソースアダプター経由でJMSプロバイダにアクセスします。この機能によってベンダーが複数アプリケーションサーバーに組み込み可能なJMSプロバイダを作成可能で、アプリケーションサーバ複数のJMSプロバイダをサポート可能になります。詳細については、Java EE Connectorアーキテクチャ仕様 v1.7を参照してください。

45.2 Basic JMS API Concepts

このセクションでは最も基本的なJMS APIの概念を解説し、JMS APIを使うシンプルなアプリケーションクライアントを作り始めるのに必要な知識を学びます。

その次のセクションではJMS APIのプログラミングモデルを説明します。さらに次のセクションでは高度な概念について触れ、これにはメッセージドリブンビーンを使うアプリケーションを作成するのに必要な事項が含まれます。

45.2.1 JMS API Architecture

JMSアプリケーションは以下の要素で構成します。

  • JMSプロバイダ(JMS provider)とはJMSインタフェースを実装するメッセージングシステムで管理や制御機能を持ちます。フルプロファイルをサポートするJava EEプラットフォーム実装にJMSプロバイダは含まれます。
  • JMSクライアント(JMS clients)とはメッセージのproduceとconsumeを行うJavaで書かれたプログラムやコンポーネントのことです。Java EEアプリケーションコンポーネントはJMSクライアントして振る舞えます。
    また、Java SEアプリケーションもJMSクライアントとして振る舞うことが可能です。GlassFish Serverドキュメント(https://glassfish.java.net/docs/)のMessage Queue Developer's Guide for Java Clientsにやり方が書いてあります。
  • メッセージ(Messages)とはJMSクライアント間で情報を通信するオブジェクトのことです。
  • 管理対象オブジェクト(Administered objects)とはクライアントが使うために設定したJMSオブジェクトのことです。JMS管理対象オブジェクトには二種類あり、それはdestinationsとコネクションファクトリー(connection factories)で、JMS Administered Objectsで解説します。管理対象オブジェクトはGlassFish Serverのparticular installation*2を使うすべてのアプリケーションに利用可能なオブジェクトを生成可能です。もしくは、開発者は個々のアプリケーションに固有のオブジェクトを生成するためにアノテーションを使うことも出来ます。

Figure 45-2は三要素の相互作用が描かれています。管理ツールアノテーションでdestinationsとコネクションファクトリーをJNDI名前空間にバインドします。そうすると、JMSクライアントは名前空間の管理対象オブジェクトのリソースインジェクションが可能になり、JMSプロバイダを介して同オブジェクトへの論理コネクションが確立できます。

Figure 45-2 JMS API Architecture

Figure 45-2 JMS API Architecture

Description of "Figure 45-2 JMS API Architecture"

45.2.2 Messaging Styles

JMS API以前では、大抵のメッセージングプロダクトがPTP(point-to-point)*3かpublish/subscribeのうちどちらかのスタイルをサポートしていました。JMS仕様は各スタイルごとのコンプライアンスを定義しています。JMSプロバイダは両方のスタイルの実装が必須であり、JMS APIはそれぞれのスタイルに固有のインタフェースを提供します。以降のサブセクションではこれらのメッセージングスタイルについて解説します。

とはいえ、JMS APIでは二つのスタイルのうち一つだけを使う必要はありません。PTPかpub/subのどちらかを使用して送受信するのに同じコードで構いません。あるdestinationが一つのスタイルだけサポートしていると、アプリケーションの振る舞いはqueueかtopicのどちらを使うかに部分的に依存します。しかし、コードそれ自体は両方のスタイルに共通で使用可能で、これによりアプリケーションは柔軟で再利用可能になります。このチュートリアルでは、JMS 2.0が提供する極めてシンプルなAPIを使用して、そうしたコーディング方法を図を用いながら解説します。

45.2.2.1 Point-to-Point Messaging Style

point-to-point (PTP)プロダクトないしアプリケーションとは、メッセージキュー(message queues), 送信者(senders), 受信者(receivers)の概念で構成されます。各メッセージは指定のキューに向けられ、受信クライアントはメッセージ保持のために確立したキューからメッセージを引き出します。キューはメッセージが受信あるいは期限切れするまですべてのメッセージを保持し続けます。

PTPメッセージングでは、Figure 45-3に示すように、以下の特徴を持ちます。

  • メッセージそれぞれが一つのコンシューマを持つ。
  • クライアントがメッセージ送信したとき、クライアントが稼動中かどうかに関わらず、受信者はメッセージをフェッチ可能。

Figure 45-3 Point-to-Point Messaging

Figure 45-3 Point-to-Point Messaging

Description of "Figure 45-3 Point-to-Point Messaging"

すべての送信メッセージが一つのコンシューマーによって正常に処理されることが必須の場合、PTPメッセージングを使います。

45.2.2.2 Publish/Subscribe Messaging Style

パブリッシュ/サブスクライブ(publish/subscribe) (pub/sub)プロダクトないしアプリケーションでは、クライアントはトピック(topic)のメッセージを処理し、トピックとは電子掲示板のような機能を持ちます。パブリッシャ(Publishers)とサブスクライバ(subscribers)は動的にトピックへパブリッシュ/サブスクライブします。トピックの複数のパブリッシャから、複数のサブスクライバへと、到着するメッセージの配信をシステムが担当します。トピックはサブスクライバへ配信をし続ける限りメッセージを保持します。

pub/subメッセージングでは、トピックにサブスクライブするコンシューマ(サブスクライバ)と、生成されるサブスクリプションの区別が重要です。コンシューマはアプリケーション内のJMSオブジェクトですが、サブスクリプションはJMSプロバイダ内のエンティティです。通常、トピックは多数のコンシューマを抱えますが、サブスクリプションは唯一つのサブスクライバを持ちます。ただし、共有サブスクリプションを生成可能で、詳細はCreating Shared Subscriptionsを参照してください。pub/subメッセージングのセマンティクスの詳細についてはConsuming Messages from Topicsを参照してください。

Pub/subメッセージングは以下の特徴を持ちます。

  • 一つのメッセージを複数のコンシューマに届けることが可能*4
  • トピックをサブスクライブするクライアントはサブスクリプションの生成に送信メッセージのみコンシューム可能で、コンシューマがメッセージをコンシュームするにはアクティブ状態を維持する必要があります。
    JMS APIではアプリケーションがdurable subscriptionsを生成することでこの要求をある程度緩和しており、コンシューマが非アクティブの間にdurable subscriptionsがメッセージを受信します。durable subscriptionsはキューの信頼性と柔軟性を提供しますが、クライアントは変わらず複数の受信者に向けてメッセージを送信可能です。durable subscriptionsの詳細についてはCreating Durable Subscriptionsを参照してください。

メッセージを複数(かゼロ)個のコンシューマで処理したい場合にpub/subメッセージングを使います。pub/subメッセージングをFigure 45-4に図示します。

Figure 45-4 Publish/Subscribe Messaging

Figure 45-4 Publish/Subscribe Messaging

Description of "Figure 45-4 Publish/Subscribe Messaging"

45.2.3 Message Consumption

メッセージングプロダクトは本質的に非同期です。メッセージのプロダクトとコンシュームに時間的な依存性は基本的にありません。しかし、JMS仕様では厳密にこれらの用語を用いています。メッセージは二つの方法のどちらかでコンシュームします。

  • 同期(Synchronously): コンシューマはreceiveメソッドを呼ぶことでdestinationから明示的にメッセージをフェッチします。receiveメソッドはメッセージが到着するまでブロックするか、指定時間内に未到着の場合はタイムアウトします。
  • 非同期(Asynchronously): アプリケーションクライアントあるいはJava SEクライアントはコンシューマにメッセージリスナー(message listener)を登録します。メッセージリスナーはイベントリスナーに似ています。メッセージがdestinationに到着すると、JMSプロバイダはリスナのonMessageメソッドを呼ぶことでメッセージを配信します。onMessageはメッセージ内容に基づいた動作を行います。Java EEアプリケーションでは、メッセージドリブンビーンがメッセージリスナー(onMessageも持つ)として動作しますが、クライアントはこれをコンシューマに登録する必要はありません。

    45.3 The JMS API Programming Model

    JMSアプリケーションの基本的な構成要素は以下の通りです。

  • 管理対象オブジェクト(Administered objects)。コネクションファクトリーとdestinations

  • コネクション(Connections)
  • セッション(Sessions)
  • JMSContextオブジェクト。オブジェクトのセッションとコネクションとを結びつける。
  • メッセージ・プロデューサ(Message producers)
  • メッセージ・コンシューマ(Message consumers)
  • メッセージ(Messages)

Figure 45-5はこれらのオブジェクトをJMSクライアントアプリケーションで組み合わせる方法を示しています。

Figure 45-5 The JMS API Programming Model

Figure 45-5 The JMS API Programming Model

Description of "Figure 45-5 The JMS API Programming Model"

また、JMSはキューブラウザ(queue browsers)も提供し、これによりアプリケーションでキュー上のメッセージをブラウズ出来ます。

このセクションではこれらのオブジェクトの簡潔な説明とサンプルコマンドおよびオブジェクトの作成と使用方法について説明します。

このサンプルはChapter 46, "Java Message Service Examples," に出てくるアプリケーション中のすべてのオブジェクトの組み合わせ方について説明します。詳細については、Java EE APIドキュメントのJMS APIの項を参照してください。

45.3.1 JMS Administered Objects

JMSアプリケーションのdestinationsとコネクションファクトリーについては通常プログラムではなく管理者がメンテナンスします。この基礎となるオブジェクトは他のJMS APIの実装要素とは少々異なります。それゆえ、オブジェクトの管理はプロバイダによって異なる管理タスクとの位置付けです。

JMSクライアントはポータブルなインタフェースを通して管理対象オブジェクトにアクセスするため、クライアントアプリケーションは他のJMS API実装下において少々の変更かあるいは変更無しに動作させられます。通常、管理者はJNDI名前空間に管理対象オブジェクトを設定し、JMSクライアントはリソースインジェクションでそのオブジェクトにアクセスします。

GlassFish Serverでは、コネクターリソース形式でJMS管理対象オブジェクトを生成するのにAdministration Consoleかasadmin create-jms-resourceコマンドを使います。また、アプリケーションにバンドルするglassfish-resources.xmlという名前のファイルにリソースを定義することも出来ます。

NetBeans IDEにはGlassFish Server用のJMSリソースを生成するウィザードがあります。詳細についてはCreating JMS Administered Objects を参照してください。

Java EEプラットフォーム仕様では開発者がアノテーションやデプロイメント記述子で管理対象オブジェクトを生成することを許可しています。この方法で生成したオブジェクトは、それを生成したアプリケーションに固有のものとなります。詳細についてはCreating Resources for Java EE Applicationsを参照してください。デプロイメント記述子の定義はアノテーションをオーバーライドします。

45.3.1.1 JMS Connection Factories

コネクションファクトリー(connection factory)とはプロバイダへのコネクションを生成するためにクライアントが使用するオブジェクトです。コネクションファクトリーは管理者が定義した接続設定パラメータをカプセル化します。コネクションファクトリーはConnectionFactory, QueueConnectionFactory, TopicConnectionFactoryインタフェースいずれかのインスタンスです。コネクションファクトリーの生成方法はCreating JMS Administered Objectsを参照してください。

通常のJMSクライアントプログラム開始時には、コネクションファクトリーリソースをConnectionFactoryオブジェクトにインジェクトします。Java EEサーバーは論理JNDI名java:comp/DefaultJMSConnectionFactoryでJMSコネクションファクトリーの提供が必須です。実JNDI名は実装固有です。

例として、以下のコードはデフォルトのコネクションファクトリーをルックアップしてConnectionFactoryオブジェクトに割り当てます。

@Resource(lookup = "java:comp/DefaultJMSConnectionFactory")
private static ConnectionFactory connectionFactory;

45.3.1.2 JMS Destinations

destinationとは、クライアントがメッセージのプロデュース先およびするメッセージのコンシューム元を指定するためのオブジェクトです。PTPメッセージングスタイルでは、destinationはキューと呼称します。pub/subメッセージングスタイルでは、destinationはトピックと呼称します。JMSアプリケーションは複数のキューもしくはトピック(もしくは両方)を使用可能です。destinationリソースの生成方法についてはCreating JMS Administered Objectsを参照してください。

GlassFish Serverでdestinationを生成するには、destinationにJNDI名を指定するJMS destinationリソースを作成します。

JMSのGlassFish Server実装では、個々のdestinationリソースは物理destinationを参照します。明示的に物理destinationを作成できますが、そうしなかった場合、Application Serverが必要に応じて作成し、また、destinationリソース削除時に物理destinationを削除します。

通常、コネクションファクトリーリソースのクライアントプログラムへのインジェクトに加えて、destinationリソースもインジェクトします。コネクションファクトリーとは異なり、destinationはPTPかpub/subのメッセージングスタイルどちらかに固有です。コードをトピックとキューの両方で共通にするには、Destinationオブジェクトにdestinationを割り当てます。

以下のコードは二つのリソース、キュートとトピックを指定しています。リソース名はJNDI名前空間に作成したdestinationリソースに対応します。

@Resource(lookup = "jms/MyQueue")
private static Queue queue;

@Resource(lookup = "jms/MyTopic")
private static Topic topic;

通常、Java EEアプリケーションでは、JMS管理対象オブジェクトはjmsネーミングサブコンテキストに位置します。共通インタフェースでは、コネクションファクトリーとdestinationの混合が可能です。つまり、ConnectionFactoryインタフェースに加え、QueueConnectionFactoryリソースをインジェクトしてTopicを使用したり、TopicConnectionFactoryリソースをインジェクトしてQueueを使用出来ます。アプリケーションの振る舞いは使用するdestinationの種類に依存し、コネクションファクトリの種類には依存しません。

45.3.2 Connections

コネクション(connection)とはJMSプロバイダの仮想コネクションをカプセル化したものです。たとえば、コネクションはクライアントとプロバイダサービスデーモン間をTCP/IPソケットで表現できます。一つ以上のセッションを作成するのにコネクションを使います。

Note
Java EEプラットフォームでは、単一コネクションから複数セッションを作る機能はアプリケーションクライアントには制限をかけています。webとEJBコンポーネントでは、コネクションは1セッションのみ作成可能です。

通常、JMSContextオブジェクトを生成することでコネクションを作成します。詳細についてはJMSContext Objectsを参照してください。

45.3.3 Sessions

セッション(session)とは、メッセージのプロデュースとコンシュームを行うためのシングルスレッドなコンテキストです。

通常、JMSContextオブジェクトを生成することで(同様にコネクションも)セッションを生成します。詳細はJMSContext Objectsを参照してください。セッションの使い道としては、メッセージ・プロデューサ, メッセージ・コンシューマ, メッセージ, キューブラウザ, temporary destinations、があります。

セッションはメッセージリスナーの実行をシリアライズ化します。詳細については、JMS Message Listenersを参照してください。

セッションはトランザクショナルコンテキストを提供し、これは送受信セットをアトミックな作業単位にグループ化します。詳細については、JMS Local Transactionsを参照してください。

45.3.4 JMSContext Objects

JMSContextオブジェクトはコネクションと単一オブジェクトのセッションを結びつけます。つまり、JMSプロバイダへのアクティブコネクションとメッセージ送受信用のシングルスレッドコンテキストを提供します。

以下のオブジェクトを生成するのにJMSContextを使います。

  • メッセージ・プロデューサ(Message producers)
  • メッセージ・コンシューマ(Message consumers)
  • メッセージ(Messages)
  • キューブラウザ(Queue browsers)
  • Temporary queues and topics (see Creating Temporary Destinations)

JMSContextはtry-with-resourcesブロックで作成できます。

JMSContextを作成するには、コネクションファクトリーのcreateContextメソッドを呼びます。

JMSContext context = connectionFactory.createContext();

アプリケーションクライアントやJava SEクライアントで引数無しに呼び出すときや、アクティブなJTAトランザクションが無いJava EE webないしEJBコンテナで呼び出す場合、createContextメソッドJMSContext.AUTO_ACKNOWLEDGE確認モード(acknowledgment mode)で非トランザクションなセッションを生成します。アクティブなJTAトランザクションが有るwebないしEJBコンテナで引数無しに呼び出す場合、createContextトランザクションセッションを生成します。Java EEアプリケーションでJMSトランザクションがどのように動作するのかについては、Using the JMS API in Java EE Applicationsを参照してください。

アプリケーションクライアントやJava SEクライアントでトランザクションセッションを生成するにはcreateContextメソッド引数にJMSContext.SESSION_TRANSACTEDを指定します。

JMSContext context = 
        connectionFactory.createContext(JMSContext.SESSION_TRANSACTED);

セッションはローカルトランザクションを使用します。詳細についてはUsing JMS Local Transactionsを参照してください。

または、デフォルトでは無い確認モードを指定可能です。詳細についてはControlling Message Acknowledgmentを参照してください。

JMSContextを使用する場合、通常、メッセージ配信はコンシューマを生成するとすぐに開始します。詳細はJMS Message Consumersを参照してください。

try-with-resourcesブロックでJMSContextを生成する場合、明示的にクローズする必要はありません。tryブロック終了時にクローズします。アプリケーションではすべてのJMSアクティビティをtry-with-resourcesブロックに入れるようにしてください。try-with-resourcesブロックを使わない場合、使い終えたらJMSContextcloseメソッドをよぶことでコネクションのクローズを絶対にしてください。

45.3.5 JMS Message Producers

メッセージ・プロデューサ(message producer)とは、JMSContextもしくはセッションが生成するオブジェクトで、destinationにメッセージを送信するために使います。JMSProducerインタフェースを実装するJMSContextがメッセージ・プロデューサを生成します。

try (JMSContext context = connectionFactory.createContext();) {
    JMSProducer producer = context.createProducer();
    ...

JMSProducerは重要なリソースを消費しない軽量なオブジェクトです。よって、変数にJMSProducerを格納して節約する必要はありません。メッセージ送信のたびに新規オブジェクトを生成して構いません。sendメソッド使用して特定のdestinationにメッセージを送ります。

context.createProducer().send(dest, message);

上記のように、送信前にメッセージを変数に格納しておいたり、sendに含めることも出来ます。詳細はJMS Messagesを参照してください。

45.3.6 JMS Message Consumers

メッセージ・コンシューマ(message consumer)とは、JMSContextもしくはセッションが生成するオブジェクトで、destinationに送信されたメッセージを受信するために使います。

JMSConsumerインタフェースを実装するJMSContextがメッセージ・コンシューマ*5を生成します。メッセージ・コンシューマを生成する最も簡単な方法はJMSContext.createConsumerメソッドを使います。

try (JMSContext context = connectionFactory.createContext();) {
    JMSConsumer consumer = context.createConsumer(dest);
    ...

メッセージ・コンシューマによってJMSクライアントはJMSプロバイダを用いてdestinationにinterest*6を登録できます。JMSプロバイダはdestinationから登録コンシューマへのメッセージ配信を管理します。

メッセージ・コンシューマを作成するのにJMSContextを使う場合、メッセージ配信はコンシューマ作成後すぐに開始します。この振る舞いをオフにするにはJMSContext生成時にsetAutoStart(false)を呼び、そうするとstartメソッドの明示的な呼び出しでメッセージ配信が開始します。コネクション切断時に一時的にメッセージ配信をストップしたい場合、stopを呼び、リスタートにはstartを呼びます。

同期的にメッセージをコンシュームするにはreceiveメソッドを使います。コンシューマ生成後の任意のタイミングでこのメソッドを呼ぶことが出来ます。

引数無しあるいは0を指定する場合、メソッドはメッセージが到着するまで無制限にブロックします。

Message m = consumer.receive();
Message m = consumer.receive(0);

単純なクライアントではこれは特に問題とならないでしょう。しかし、もしメッセージが利用不可となる場合がありえるなら、タイムアウト付きで同期的な受信を使用してください。0よりタイムアウト引数でreceiveを呼び出します。推奨値は1秒です。

Message m = consumer.receive(1000); // time out after a second

アプリケーションクライアントやJava SEクライアントで非同期メッセージ配信を使用可能にするには、次のセクションで述べるメッセージリスナーを使います。

durable topic subscriptionを生成するにはJMSContext.createDurableConsumerメソッドを使います。このメソッドはトピックを使う場合にのみ有効です。詳細については、Creating Durable Subscriptionsを参照してください。なおトピックについては、共有コンシューマを生成可能です。Creating Shared Subscriptionsを参照してください。

45.3.6.1 JMS Message Listeners

メッセージリスナーとは、メッセージの非同期イベントハンドラ―の役割を持つオブジェクトです。このオブジェクトは一つのメソッドonMessageを持つMessageListenerインタフェースを実装しています。onMessageメソッドにメッセージ到着時のアクションを定義します。

アプリケーションクライアントやJava SEクライアントでは、setMessageListenerメソッドを使うことで特定のメッセージ・コンシューマにメッセージリスナーを登録します。たとえば、MessageListenerインタフェースを実装するListenerというクラスを定義する場合、以下のようにメッセージリスナーを登録します。

Listener myListener = new Listener();
consumer.setMessageListener(myListener);

メッセージ配信開始後、JMSプロバイダはメッセージが配信されると自動的にメッセージリスナーのonMessageを呼び出します。onMessageメソッドMessage型の引数を一つ取り、そのメソッド内で必要に応じて別のメッセージサブタイプにキャストします(Message Bodiesを参照)。

Java EE webやEJBコンテナでは、非同期メッセージ配信にはメッセージドリブンビーンを使います。メッセージドリブンビーンはMessageListenerインタフェースを実装しているのでonMessageメソッドを持っています。詳細については、Using Message-Driven Beans to Receive Messages Asynchronouslyを参照してください。

onMessageメソッドではすべての例外を処理するようにしてください。RuntimeExceptionのスローはプログラミングエラーと見なされます。

メッセージリスナーの簡単な例としては、Using a Message Listener for Asynchronous Message Deliveryを参照してください。Chapter 46, "Java Message Service Examples,"にはメッセージリスナーとメッセージドリブンビーンの例がいくつか掲載されています。

45.3.6.2 JMS Message Selectors

メッセージングアプリケーションで受信メッセージにフィルタをかけたい場合、JMSメッセージセレクタ(JMS message selector)を使います。これはdestination用のメッセージ・コンシューマに関心のあるメッセージを定義します。メッセージセレクタはアプリケーションではなくJMSプロバイダにメッセージフィルタリングの動作を指定します。メッセージセレクタを使用するアプリケーションの例は、Sending Messages from a Session Bean to an MDBを参照してください。

メッセージセレクタは式を持つStringです。式のシンタックスはSQL92条件式シンタックス(SQL92 conditional expression syntax)のサブセットがベースです。以下の例のメッセージセレクタNewsTypeプロパティの値がSportsOpinionのメッセージを選択します。

NewsType = 'Sports' OR NewsType = 'Opinion'

createConsumercreateDurableConsumerメソッドは共有コンシューマを生成するためのメソッドで、メッセージ・コンシューマ生成時に引数にメッセージセレクタを指定可能です。

セレクタを指定すると、メッセージ・コンシューマはセレクタにマッチするヘッダーとプロパティを持つメッセージのみ受信します。(Message HeadersMessage Properties)なお、メッセージセレクタはメッセージボディの内容に基づいたフィルタリングは出来ません。

45.3.6.3 Consuming Messages from Topics

トピックからメッセージをコンシュームするセマンティクスは、キューからのそれと比べると複雑です。

アプリケーションがトピックからメッセージをコンシュームするには、トピックにサブスクリプションを生成してそのサブスクリプションにコンシューマを生成します。サブスクリプションはdurableかnon-durableのどちらかになり、また、共有か非共有のどちらかになります。

サブスクリプションはJMSプロバイダ内のエンティティと考えてよいですが、一方、コンシューマはアプリケーション内のJMSオブジェクトです。

サブスクリプションは、サブスクリプション生成後にトピックに送信されたすべてのメッセージのコピーを、メッセージセレクタを指定していなければ、受信します。メッセージセレクタを指定している場合、セレクタにマッチするプロパティを持つメッセージだけがサブスクリプションに追加されます。

非共有サブスクリプションは単一コンシューマに制限されます。この場合、サブスクリプションのすべてのメッセージがコンシューマに配信されます。共有サブスクリプション複数のコンシューマを許容します。この場合、サブスクリプションの個々のメッセージは唯一つのコンシューマへ配信されます。JMSは同一サブスクリプション上の複数コンシューマ間にメッセージがどのように配信されるのかについては定義しません。

サブスクリプションはdurableかnon-durableのどちらかになります。

non-durable subscriptionはサブスクリプション上のアクティブなコンシューマがある場合に限り存在します。つまり、コンシューマが存在してオープンしている間だけ、トピックに送信されたメッセージがサブスクリプションに追加されることになります。

non-durable subscriptionは非共有か共有のどちらかになります。

  • 非共有non-durable subscriptionは名前を持たず単一のコンシューマ・オブジェクトだけを持つことが可能です。コンシューマ・オブジェクト生成時に自動的に生成が行われます。また、コンシューマ・オブジェクトクローズ時に自動削除して永続化しません。
    JMSContext.createConsumerメソッドは、トピックをサブスクリプションとして指定する場合、非共有nondurable subscription上にコンシューマを生成します。
  • 共有non-durable subscriptionは名前とオプションのクライアント識別子(client identifier)で識別を行い、メッセージをコンシュームするオブジェクトを複数持ちます。最初のコンシューマ・オブジェクト生成時に自動的に生成されます。最後のコンシューマ・オブジェクトがクローズする時に自動削除して永続化しません。詳細についてはCreating Shared Subscriptionsを参照してください。

サブスクリプションをdurableにするのは高いオーバーヘッドコーストを伴います。durable subscriptionは永続化されて明示的に削除するまでメッセージを蓄積し続けます。この動作はたとえメッセージをコンシュームするオブジェクトが無い場合でも同様です。詳細はCreating Durable Subscriptionsを参照してください。

45.3.6.4 Creating Durable Subscriptions

pub/subアプリケーションですべての送信メッセージの受信を保証するには、トピックのコンシュームにdurable subscriptionを使います。

nondurable subscription同様、durable subscriptonも非共有か共有のどちらかになります。

  • 非共有durable subscriptionは名前およびクライアント識別子(which must be set)*7で識別を行い、単一のコンシューマ・オブジェクトにのみ関連付けられます。
  • 共有durable subscriptionは名前およびオプションのクライアント識別子で識別を行い、複数のコンシューマ・オブジェクトを持てます。

A durable subscription that exists but that does not currently have a non-closed consumer object associated with it is described as being inactive.*8

コンシューマないし非共有durable subscriptionを生成するにはJMSContext.createDurableConsumerメソッドを使います。非共有durable subscriptionは同時に一つだけのアクティブなコンシューマを持てます。

JMSプロバイダが保持するユニーク識別子を指定することでメッセージをコンシュームするdurable subscriptionを識別します。同一識別子を持つそれ以降のコンシューマ・オブジェクトはそれ以前のコンシューマが残した状態でサブスクリプションを再開します。durable subscriptionにアクティブなコンシューマが一つも無い場合、JMSプロバイダはサブスクリプションが受信を始めるか期限切れになるまでメッセージを保持し続けます。

以下のように設定することで非共有durable subscriptionのユニーク識別子を確立します。

コマンドラインもしくはAdministration Consoleのどちらかを使用してクライアント固有のコネクションファクトリー用のクライアントIDを管理者が作成します(アプリケーションクライアントもしくはJava SEクライアントでは、代わりにJMSContext.setClientIDを使います)。

JMSContextを生成するのにコネクションファクトリーを使い、二つの引数でcreateDurableConsumerを呼びます。引数は、トピックとサブスクリプション名です。

String subName = "MySub";
JMSConsumer consumer = context.createDurableConsumer(myTopic, subName);

コンシューマ生成後にサブスクリプションはアクティブになります。その後の適当なタイミングで、コンシューマをクローズします。

consumer.close();

JMSプロバイダはトピックに送信されたメッセージを格納し、これはキューにメッセージを保存するのと同様な動作です。プログラムあるいは別のアプリケーションが、同じコネクションファクトリー・クライアントID・同じトピック・同じサブスクリプション名、を使用してcreateDurableConsumerを呼びだすと、サブスクリプションは再アクティブ化し、JMSプロバイダはサブスクリプションが非アクティブの間に送信されたメッセージを配信します。

durable subscriptionを削除するには、まずコンシューマをクローズし、それから引数にサブスクリプション名を与えてunsubscribeメソッドを呼びます。

consumer.close();
context.unsubscribe(subName);

unsubscribeメソッドはプロバイダがサブスクリプション用にメンテナンスしている状態を削除します。

Figure 45-6Figure 45-7はnondurableとdurable subscriptionの違いを示しています。通常、nondurable subscriptionでは、コンシューマとサブスクリプションの開始と終了は同一時点で、実質的には同一です。コンシューマがクローズするとき、サブスクリプションもまた終了します。createは引数にTopicを与えるJMSContext.createConsumeを呼び出し、closeJMSConsumer.closeを呼び出します。あるclose時点から次のcreate時点までの間にトピックに送信されたメッセージはどちらのサブスクリプションにも追加されません。Figure 45-6では、コンシューマはM1, M2, M5, M6は受信しますが、M3, M4は受信しません。

Figure 45-6 Nondurable Subscriptions and Consumers

Figure 45-6 Nondurable Subscriptions and Consumers

Description of "Figure 45-6 Nondurable Subscriptions and Consumers"

durable subscriptionでは、コンシューマはクローズと再作成が可能で、ただしサブスクリプションは存在し続けてアプリケーションがunsubscribeメソッドを呼ぶまでメッセージをホールドします。Figure 45-7では、createJMSContext.createDurableConsumerを呼び出し、closeJMSConsumer.closeを呼び出し、unsubscribeJMSContext.unsubscribeを呼び出します。最初のコンシューマがクローズしたあとに送信されたメッセージは次のコンシューマ作成時に(同一のdurable subscriptionで)受信します。そのため、コンシューマが存在しない間に到着したメッセージM2, M4, M5は失われません。

Figure 45-7 Consumers on a Durable Subscription

Figure 45-7 Consumers on a Durable Subscription

Description of "Figure 45-7 Consumers on a Durable Subscription"

共有durable subscriptionではメッセージ受信に複数コンシューマを使用可能です。共有durable subscriptionを使う場合、コネクションファクトリーはクライアント識別子を持つ必要がありません。共有durable subscriptionを作成するには、JMSContext.createSharedDurableConsumerメソッドの引数にトピックとサブスクリプション名を指定します。

JMSConsumer consumer = 
        context.createSharedDurableConsumer(topic, "MakeItLast");

durable subscriptionを使用するJava EEアプリケーションの例については、Acknowledging Messages, Using Durable Subscriptions, Using Shared Durable Subscriptions, Sending Messages from a Session Bean to an MDBを参照してください。

45.3.6.5 Creating Shared Subscriptions

createConsumerないしcreateDurableConsumerメソッドで作成するトピック・サブスクリプションは唯一つだけのコンシューマを持てます(ただしトピックは複数持てる)。同一トピックからコンシュームする複数クライアントは、当然、トピックに複数サブスクリプションを持ちます。そして、トピックに送信されるすべてのメッセージをすべてのクライアントが受信します(メッセージセレクタ―でフィルタする場合を除く)。

また、createSharedConsumerメソッドを使用してトピックにnondurable shared subscriptionを生成可能で、引数にはdestinationサブスクリプション名を指定します。

consumer = context.createSharedConsumer(topicName, "SubName");

共有サブスクリプションでは、メッセージは同一トピックとサブスクリプション名を使用する複数クライアントに配信します。トピックに送信されるメッセージはすべてのサブスクリプションに追加されますが(メッセージセレクタ依存)、サブスクリプションに追加されたメッセージはサブスクリプション上のコンシューマの一つだけに配信し、よって、クライアントの一つだけがメッセージを受信します。共有サブスクリプションが有用なのは、個々のメッセージを受信するサブスクリプションにコンシューマを一つだけ持つのではなく、サブスクリプション複数のコンシューマでメッセージのロードを共有したい場合です。この機能によりJava EEアプリケーションクライアントとJava SEアプリケーションのスケーラビリティを向上出来ます。(メッセージドリブンビーンは複数スレッド間でトピックからのメッセージ処理を共有します。)

shared nondurable consumersを使用する簡単な例はUsing Shared Nondurable Subscriptionsを参照してください。

また、JMSContext.createSharedDurableConsumerでshared durable subscriptionsを作成できます。詳細についてはCreating Durable Subscriptionsを参照してください。

45.3.7 JMS Messages

JMSアプリケーションの根本的な目的はメッセージのプロデュースとコンシュームで、これによりメッセージは他のソフトウェアアプリケーションで使用可能になります。JMSメッセージはシンプルで非常に柔軟な基本フォーマットを持ち、異種プラットフォーム上の非JMSアプリケーションのフォーマットにマッチするメッセージを作成出来ます。

JMSメッセージは三つの要素に分かれ、ヘッダ・プロパティ・ボディ、となります。ヘッダーのみ必須です。以下のセクションで三つの要素について解説します。

メッセージヘッダ・プロパティ・ボディの完全なドキュメントについてはAPIドキュメントのMessageインタフェースを参照してください。使用可能なメッセージタイプのリストについてはMessage Bodiesを参照してください。

45.3.7.1 Message Headers

JMSメッセージヘッダーには定義済みフィールドがいくつか含まれ、その値はクライアントとプロバイダでメッセージの送信と識別に使われます。Table 45-1はJMSメッセージヘッダーフィールドのリストで、内容と設定方法について記しています。たとえば、すべてのメッセージはユニークの識別子を持ち、その値はJMSMessageIDヘッダーフィールドで表現します。その他には、JMSDestinationヘッダーフィールドはメッセージ送信先のキューないしトピックを表現します。それ以外のフィールドにはタイムスタンプと優先レベルを持つものがあります。

それぞれのフィールドはgetter/setterと関連付けられており、ドキュメントはMessageインタフェースにあります。クライアントがセットするヘッダーフィールドもありますが、大半はsendメソッドが自動的にセットするもので、クライアントがセットした値をsendでオーバーライドします。

Table 45-1 How JMS Message Header Field Values Are Set

ヘッダーフィールド 説明 設定箇所
JMSDestination メッセージ送信先のdestination JMSプロバイダのsendメソッド
JMSDeliveryMode メッセージ送信後に指定される配信モード(Specifying Message Persistenceを参照) JMSプロバイダのsendメソッド
JMSDeliveryTime メッセージ送信後の配信ディレイを加えたメッセージ送信時間(Specifying a Delivery Delayを参照) JMSプロバイダのsendメソッド
JMSExpiration メッセージの有効期限時間(Allowing Messages to Expire JMSプロバイダのsendメソッド
JMSPriority メッセージのプライオリティ(Setting Message Priority Levelsを参照) JMSプロバイダのsendメソッド
JMSMessageID プロバイダが送信するメッセージごとにユニークな識別子の値 JMSプロバイダのsendメソッド
JMSTimestamp 送信のためにメッセージがプロバイダへ渡された時間 JMSプロバイダのsendメソッド
JMSCorrelationID あるメッセージから別のメッセージへのリンク値。通常はJMSMessageIDの値が使われる。 クライアントアプリケーション
JMSReplyTo 送信メッセージに対する返信先のDestination クライアントアプリケーション
JMSType クライアントアプリケーションから与えられる型識別子 クライアントアプリケーション
JMSRedelivered メッセージを再配信するかどうか 配信前のJMSプロバイダ
45.3.7.2 Message Properties

必要であればヘッダーフィールドに加えてメッセージにプロパティを設定可能です。他のメッセージングシステムとの互換性や、メッセージ・セレクタでプロパティを使用可能です(JMS Message Selectorsを参照)。メッセージ・セレクタとしてプロパティを使う例については、Sending Messages from a Session Bean to an MDBを参照してください。

JMS APIにはJMSXで始まる名前の定義済みプロパティが存在します。そのうち、JMSプロバイダでの実装が必須なのはJMSXDeliveryCount(メッセージの配信回数)で、残りは任意です。定義済みプロパティやアプリケーションにおけるユーザ定義プロパティの使用は任意です。

45.3.7.3 Message Bodies

JMS APIは6つのメッセージ型を定義しています。それぞれのメッセージ型は異なるメッセージボディに対応します。これによりメッセージを異なる形式でデータ送受信が可能となります。メッセージ型の解説は以下のTable 45-2の通りです。

Table 45-2 JMS Message Types

メッセージ型 ボディの内容
TextMessage java.lang.Stringオブジェクト(例:XMLファイルの中身)。
MapMessage name-valueペアのセット。nameはStringオブジェクトでvalueはJavaプログラミング言語のプリミティブ型。エントリはenumeratorによるシーケンシャルアクセスかnameによるランダムアクセスが可能。エントリの順序は未定義。
BytesMessage uninterpreted byteのストリーム。このメッセージ型は既存のメッセージフォーマットと一致するようにボディをエンコーディングするのに使われる。
StreamMessage Javaプログラミング言語のプリミティブ値のストリームで、プリミティブ値で埋まったストリームからシーケンシャルに読み込む。
ObjectMessage Javaプログラミング言語Serializableオブジェクト。
Message ボディなし。ヘッダーフィールドとプロパティだけで構成されている。このメッセージ型はメッセージボディが不要な場合に便利。

JMS APIには型ごとにボディを埋めてメッセージを作成するメソッドがあります。たとえば、TextMessageを作成して送信するには、以下のようにします。

TextMessage message = context.createTextMessage();
message.setText(msg_text);     // msg_text is a String
context.createProducer().send(message);

コンシューム時には、メッセージはジェネリックMessageオブジェクトとして到着します。そのオブジェクトを適切なメッセージ型にキャストし、ボディにアクセスするメソッドを使ったり、メッセージ内容を展開したりします(必要に応じてヘッダーとプロパティも使用可能)。たとえば、BytesMessageのストリーム指向のreadメソッドを使いたい、とします。そのためにはStreamMessageボディを参照するために適切なメッセージ型へのキャストが必須です。

メッセージ型へメッセージをキャストする代わりに、引数にメッセージ型を指定するMessagegetBodyメソッドを呼ぶことも可能です。例えば、StringとしてTextMessageを取得可能です。以下のコードはgetBodyの使用例です。

Message m = consumer.receive();
if (m instanceof TextMessage) {
    String message = m.getBody(String.class);
    System.out.println("Reading message: " + message);
} else {
    // Handle error or process another message type
}

JMS APIではTextMessage, BytesMessage, MapMessage, ObjectMessageの生成と受信用のショートカットを提供しています。例えば、TextMessageで文字列をラップするのではなく、文字列を直接送受信出来ます。文字列を送信する例は以下の通りです。

String message = "This is a message";
context.createProducer().send(dest, message);

receiveBodyメソッドでメッセージを受信できます。

String message = receiver.receiveBody(String.class);

receiveBodyメソッドStreamMessageMessageを除くメッセージ型を受信可能です。ただし、メッセージボディが特定の型にアサイン可能な場合に限ります。

アプリケーションに単純なシグナルのメッセージを送信したい場合には空のMessageが便利です。Chapter 46, "Java Message Service Examples,"の例では、いくつかのテキストメッセージの後に空のメッセージを送信しています。

context.createProducer().send(dest, context.createMessage());

コンシューマー側のコードでは、すべてのメッセージを受信したことを示すシグナルとして非テキストメッセージを処理します。

Chapter 46, "Java Message Service Examples," の例では、TextMessage, MapMessage, Message型のメッセージを使用しています。

45.3.8 JMS Queue Browsers

キューに送信したメッセージは、そのキュー用のメッセージコンシューマがコンシュームするまで、キューに残ります。JMS APIではキューのメッセージと各メッセージのヘッダー値を参照可能なQueueBrowserオブジェクトを提供しています。QueueBrowserオブジェクトを作成するには、JMSContext.createBrowserメソッドを使います。

QueueBrowser browser = context.createBrowser(queue);

QueueBrowserの使用例についてはBrowsing Messages on a Queueを参照してください。

createBrowserQueueBrowser作成時に第二引数としてメッセージセレクタを指定できます。メッセージセレクタについてはJMS Message Selectorsを参照してください。

JMS APIではトピック参照用のメカニズムは用意していません。通常、メッセージは到着後すぐにトピックから姿を消します。もしメッセージをコンシュームするコンシューマが存在しない場合、JSMプロバイダはメッセージを削除します。durable subscriptionsであればメッセージコンシューマが非アクティブの間はメッセージは残るものの、JMSではそのメッセージを参照するための機能は定義していません。

45.3.9 JMS Exception Handling

JMS APIのすべてのチェック例外のルートクラスはJMSExceptionです。JSM APIのすべての非チェック例外のルートはJMSRuntimeExceptionです。

JMSExceptionJMSRuntimeExceptionをキャッチすることは、JMS APIに関するすべての例外処理の汎用的な方法の一つです。

JMSExceptionJMSRuntimeExceptionには以下のサブクラスがあり、詳細はAPIドキュメントを参照してください。

  • IllegalStateException, IllegalStateRuntimeException
  • InvalidClientIDException, InvalidClientIDRuntimeException
  • InvalidDestinationException, InvalidDestinationRuntimeException
  • InvalidSelectorException, InvalidSelectorRuntimeException
  • JMSSecurityException, JMSSecurityRuntimeException
  • MessageEOFException
  • MessageFormatException,MessageFormatRuntimeException
  • MessageNotReadableException
  • MessageNotWriteableException, MessageNotWriteableRuntimeException
  • ResourceAllocationException, ResourceAllocationRuntimeException
  • TransactionInProgressException, TransactionInProgressRuntimeException
  • TransactionRolledBackException, TransactionRolledBackRuntimeException

チュートリアルのサンプルではJMSExceptionもしくはJMSRuntimeExceptionをキャッチしています。

続き→ The Java EE 7 TutorialのJava Message Service Conceptsの章をテキトーに訳した・2 - kagamihogeの日記

*1:訳的には宛先とかだけどそのままでも通じるだろうからそのまま

*2:何のことだろうか…

*3:日本語的にはP"2"Pの方が通りがよいっぽいけど、このドキュメント中ではP"T"Pと略記しているのでそっちに合わせる

*4:Each message can have multiple consumers.が原文。直訳するなら『各メッセージが複数のコンシューマを持てる』だが意味不明なのでちょっと意訳した

*5:原文ではmessage "producer"になっているがおそらく間違いと思われる。

*6:興味関心あるいはcosumerがゲットしたいモノと訳してもいいんだろうけどイマイチしっくり来る訳語が思いつかなかったのでそのまま

*7:このsetは何なのか良くわからなかった

*8:わからん