2 アプリケーション設計のベスト・プラクティス
WebLogic Server JMSの設計オプション、設計の過程で考慮すべきアプリケーションの動作、および推奨の設計パターンについて学習します。
メッセージの設計
メッセージングのパフォーマンスを向上させるためのメッセージの設計方法を学習します。
アプリケーション・オブジェクトのシリアライズ
Javaオブジェクトのシリアライズを行うと、CPUに非常に大きな負荷がかかるおそれがあります。最終的には、この負荷がJMSオブジェクト・メッセージに影響します。この負荷は、java.io.Externalizable
を実装するアプリケーション・オブジェクトを使用することである程度相殺できますが、クラス記述子のマーシャリングにおいては依然として大きなオーバーヘッドになります。Objectメッセージに組み込む追加オブジェクトのクラス記述子を記述する手間を省くには、これらのオブジェクトにExternalizable
を実装し、オブジェクト上で直接readExternal
およびwriteExternal
を呼び出します。たとえば、stream.writeObject(obj)
ではなくobj.writeExternal(stream)
を呼び出します。通常、Bytes
メッセージやStream
メッセージを使用すると、より望ましい結果が得られます。
文字列のシリアライズ
Java文字列のシリアライズは、その他のJavaプリミティブ型のシリアライズよりも大きな負荷がかかります。文字列はメモリーも多用します。1文字につき2バイトのメモリーを消費し、バイナリ・データ(整数など)をコンパクトに表現することもできません。また、文字列ベースのメッセージを使用すると、多くの場合、文字列をアプリケーションで直接使用できる形式に処理するための解析が負荷になります。したがって、Bytesメッセージ、Streamメッセージ、MapメッセージおよびObjectメッセージが、TextメッセージやXMLメッセージよりも望ましい結果になることがあります。同様に、メッセージ・プロパティでは文字列を使用しないことをお薦めします(特にそのサイズが大きい場合)。
サーバー側のシリアライズ
非永続メッセージのシリアライズの負荷は、WebLogic JMSサーバーにはかかりません。非永続メッセージ・タイプのシリアライズは、リモート・クライアントの負荷で実行されます。永続メッセージは、サーバーでシリアライズされます。
メッセージの圧縮
JMSアプリケーション内の大きなメッセージを圧縮するとパフォーマンスが向上します。
メッセージの圧縮により、ネットワーク上でのメッセージの転送に必要な時間が短縮され、JMSサーバーが使用するメモリーの量も減ります。また、永続メッセージの場合は、永続書込みのサイズも小さくなります。多くの場合、TextおよびXMLメッセージはかなり圧縮できます。ただし、圧縮を行うと、当然のことながらクライアントのCPU使用量が増えることになります。
小さいメッセージの場合は、圧縮する利点がない場合もあります。メッセージのサイズが数KB以下の場合、圧縮することで実際のサイズが大きくなることもあります。JDKには、組込みの圧縮ライブラリが用意されています。詳細については、java.util.zip
パッケージを参照してください。
指定されたしきい値のサイズを超過したメッセージの自動圧縮をJMS接続ファクトリを使用して指定する方法については、Oracle WebLogic Serverのパフォーマンスのチューニングのメッセージの圧縮に関する項を参照してください。
メッセージ・プロパティとメッセージ・ヘッダー・フィールド
ユーザー定義のメッセージ・プロパティのかわりに、標準のJMSメッセージ・ヘッダー・フィールドや、メッセージ・データのメッセージ本文を使用することも検討してください。メッセージ・プロパティは、シリアライズに余分な負荷がかかり、アクセスにおいても標準のJMSメッセージ・ヘッダー・フィールドより大きな負荷がかかります。
プロパティ・フィールドやヘッダー・フィールドに大量のデータを組み込むことも避けてください。ページングが有効になっている場合でも、ページングされるのはメッセージの本文のみです。したがって、アプリケーションにユーザー定義のメッセージ・プロパティが定義されている場合は、大きな文字列プロパティを使用しないようにしてください。
「メッセージ・ヘッダー・フィールド」および「メッセージ・プロパティ・フィールド」を参照してください。
メッセージの順序
メッセージの処理を厳格に順序付けする場合は、再配信の順序付け機能ではなく、メッセージ順序単位機能を使用することをお薦めします。
再配信の順序付けに比べ、メッセージ順序単位には以下の利点があります。
-
構成が容易であること。
-
非同期レシーバのカスタム接続ファクトリが必要ありません。たとえば、メッセージドリブンBean (MDB)を使用する場合は、
MessagingMaximum
を1に設定します。 -
分散宛先を使用する場合の、単純な構成。
-
-
処理遅延の間もメッセージの順序が維持されます。
-
トランザクションのロールバックやセッションのリカバリの間もメッセージの順序が維持されます。
再配信の順序付けを使用しているアプリケーションは、メッセージ順序単位を使用するようにアップグレードすることをお薦めします。「メッセージ順序単位の使用」を参照してください。
トピックとキュー
アプリケーションの設計を始めるときには、トピックとキューのどちらを使用したほうがよいかを即座に判断できない場合もあります。
次の条件のいずれかが満たされる場合にのみ、トピックを使用してください。
-
同じメッセージを、複数のコンシューマにレプリケートする必要があります。
-
メッセージを選択するアクティブなコンシューマがない場合に、そのメッセージを破棄する必要があります。
-
たくさんのサブスクライバがあり、それぞれに一意のセレクタがあります。
恒久サブスクライバが1つのみのトピックは、セマンティクスの面ではキューに似ています。相違点は以下のとおりです。
-
恒久サブスクライバのトピック・セレクタを変更した場合、サブスクリプション内の以前のメッセージがすべて削除されますが、コンシューマのキュー・セレクタを削除してもキュー内のメッセージは削除されません。
-
キューの場合はメッセージを複数のコンシューマにラウンドロビン方式で配信できますが、トピック・サブスクライバのコンシューマは1つに制限されています。
JMSキューとトピックの構成の詳細は、Oracle WebLogic Server JMSリソースの管理のキューおよびトピック宛先のリソースに関する項を参照してください。
非同期コンシューマと同期コンシューマ
一般的に、非同期(onMessage
)コンシューマは、パフォーマンスとスケーラビリティの面で同期コンシューマより優れています。
-
非同期コンシューマの場合、作成されるネットワーク・トラフィックが少なくなります。メッセージは、メッセージ・リスナーへのパイプラインを使用して一方向に送信されます。パイプラインでは、複数のメッセージを単一のネットワーク呼出しに集約できます。
ノート:
-
WebLogic Serverでは、JMS接続ファクトリの「同期コンシューマのプリフェッチ・モード」オプションを有効にすることで、同期コンシューマでも非同期コンシューマと同じ効率的な動作を利用できるようになります。詳細は、「プリフェッチ・モードを使用した同期メッセージ・パイプラインの作成」を参照してください
-
WebLogic Server JMSでは、同じセッションでの同期コンシューマおよび非同期コンシューマはサポートされていません。
-
-
非同期コンシューマの場合、使用するスレッドが少なくなります。非同期コンシューマは、アクティブでない間はスレッドを使用しません。同期コンシューマは、受信呼出しの間スレッドを消費します。その結果、スレッドがアイドル状態になる期間が長くなり、呼出しにブロッキング・タイムアウトが指定されている場合は特に顕著になります。
-
サーバーで実行するアプリケーション・コードでは、ほとんどの場合、非同期コンシューマを(通常はMDBを介して)使用するのが最適。非同期コンシューマを使用することで、アプリケーション・コードがサーバー上でブロッキング処理を実行するのを回避できます。ブロッキング処理は、サーバー側スレッドをアイドルにし、デッドロックの原因になることもあります。デッドロックは、ブロッキング処理がすべてのスレッドを消費したときに発生します。ブロッキング処理自体の解除処理に使用するスレッドが残っていないと、ブロッキングを解除することができなくなってしまいます。
詳細は、「クラシックAPIを使用したメッセージの非同期受信およびクラシックAPIを使用したメッセージの同期受信」を参照してください
永続メッセージと非永続メッセージ
アプリケーションを設計する際は、永続QOSが必要な場合を除いて、メッセージが非永続モードで送信されるように指定してください。
非永続モードをお薦めする理由は、同期書込みが無効になっていないかぎり、永続QOSが原因でパフォーマンスがかなり悪化する場合が多いためです。
ノート:
送信する永続メッセージを誤って永続化しないようにしてください。設計者がメッセージを非永続モードで送信するつもりで設計しても、アプリケーションが永続メッセージを送信することがあります。
メッセージが本当に非永続であれば、通常のJMSストアには格納されません。永続化されたメッセージがないことを確認するには、消費されていないメッセージがJMSサーバーに蓄積されているときに、JMSストアのサイズが大きくなっているかどうかをチェックします。以下に、メッセージの永続性を決定する条件を優先度の高い順に示します。
-
プロデューサの接続の接続ファクトリの構成:
-
PERSISTENT (デフォルト)
-
NON_PERSISTENT
-
-
QueueSenderおよびTopicPublisherでのJMSプロデューサAPIオーバーライド:
-
setDeliveryMode(DeliveryMode.PERSISTENT)
-
setDeliveryMode(DeliveryMode.NON_PERSISTENT)
-
setDeliveryMode(DeliveryMode.DEFAULT_DELIVERY_MODE) (デフォルト)
-
-
QueueSenderおよびTopicPublisherでのメッセージごとのJMSプロデューサAPIオーバーライド:
-
キューの場合は、send()の省略可能なdeliveryModeパラメータ
-
トピックの場合は、publish()の省略可能なdeliveryModeパラメータ
-
-
宛先の構成でのオーバーライド:
-
永続
-
非永続
-
配信しない(デフォルト、オーバーライドしないことを意味します)
-
-
JMSサーバーの構成でのオーバーライド:
-
ストアが構成されている場合は、ターゲットの各WebLogic Serverインスタンス上で使用できるデフォルトの永続ストアを使用することを意味します
-
ストアが構成されている場合はオーバーライドがないことを意味します。
-
-
恒久サブスクライバのみ:
-
サブスクライバがない場合や、トピックに非恒久サブスクライバしかない場合は、メッセージが非永続に格下げされます。(非恒久サブスクライバはJMSサーバーの稼動中しか存在しないため、メッセージを永続化する理由がありません。)
-
-
一時的な宛先:
-
一時的な宛先はそれらをホストするJMSサーバーの稼動中しか存在しないため、メッセージを永続化する理由がありません。WebLogic JMSでは、一時的な宛先のすべてのメッセージが自動的に非永続になります。
-
恒久サブスクライバが非永続メッセージしか受信しない場合でも、そのJMSサーバーには永続ストアを構成する必要があります。恒久サブスクリプションは、JMS仕様の要件に従って、サーバーの再起動後も継続するように永続化されます。
確認応答とコミットの延期
通常は送信よりも受信のほうに時間がかかるため、複数のメッセージを受信してまとめて確認応答できるようになるまで確認応答を延期して、受信に関わるオーバーヘッドを軽減することを検討してください。
トランザクションを使用している場合は、「確認応答」
を「コミット」
と読み替えてください。
非恒久サブスクリプションの場合はすでに内部的に最適化されているため、確認応答を延期してもパフォーマンスはあまり向上しません。
非同期リスナーには、確認応答の延期を実装できない場合があります。メッセージ10個ごとに確認応答する非同期リスナーが5個のメッセージしか受信しなかった場合、最後のいくつかのメッセージが確認応答されない可能性があります。考えられる解決策としては、onMessage()
コールバック内からの同期非ブロッキング受信を非同期コンシューマにポストさせて、後続のメッセージを受信します。別の解決策としては、wake upリスナーがトリガーされたときに、リスナーの宛先にメッセージを送信するタイマーが開始されるようにします。このメッセージが正しいリスナーに確実に転送されれば、リスナーの宛先が起動してまだ確認応答されていない未処理の作業を完了します。
非恒久サブスクライバでのAUTO_ACKの使用
トランザクション非対応の非恒久トピック・サブスクライバがメッセージのローカル・コピーをクライアント側で格納するように最適化されており、これによって確認応答の発行に伴うネットワーク・オーバーヘッドを軽減しています。
この最適化によってパフォーマンスが10 - 20%向上します。パフォーマンスの向上は、サブスクライバの負荷が大きいほどより顕著になります。
この最適化の副作用の1つは、特に数多くのトピック・サブスクライバが同時に存在する場合に、クライアント側でのガベージ・コレクションのオーバーヘッドが大きくなり、メッセージ・サブスクリプションのパフォーマンスが悪化するおそれがあることです。このような悪化を防ぐため、サブスクライバ・クライアントのヒープ・サイズを大きくすることをお薦めします。たとえば、10個のJVMで100個のサブスクライバを同時に存在させるテストでは、各JVMのヒープ・サイズの初期値と最大値を64 MBに設定すれば十分でした。
代替のサービス品質、マルチキャスト、NO_ACKNOWLEDGE
WebLogic JMSでは、パフォーマンスを改善できる代替のサービス品質(QOS)拡張が提供されています。
MULTICAST_NO_ACKNOWLEDGEの使用
非恒久トピック・サブスクライバは、MULTICAST_NO_ACKNOWLEDGE
を使用してメッセージをサブスクライブできます。トピックにこのようなサブスクライバがある場合、JMSサーバーはマルチキャスト・モードを使用してこれらのサブスクライバにメッセージをブロードキャストします。マルチキャストでは、サブスクライバごとに1つずつメッセージを処理するのではなく、サブスクライバがいくつあってもネットワークで1つのメッセージを処理するだけでよいため、パフォーマンスが格段に向上し、直線的なスケーラビリティが提供されます。ネットワークが混み合っている場合や、クライアントによるメッセージ処理が遅れている場合は、マルチキャスト・メッセージが消失するおそれがあります。マルチキャスト・メッセージでrecover()
やacknowledge()
を呼び出しても効果はありません。
ノート:
クライアント側では、マルチキャスト・セッションごとに、マルチキャスト・ソケットからメッセージを取り出すための専用のスレッドが1つ必要になります。そのため、JMSクライアント側のスレッド・プール・サイズを増やして調整する必要があります。
このQOS拡張による保証は、非恒久トピック・サブスクリプション用にOracle WebLogic Server以外のベンダーから提供されている一部のJMS実装のデフォルトQOSによる保証と同じレベルです。JMS 1.1仕様では、サブスクライバの準備が整っていない場合に非恒久トピック・メッセージを破棄(削除)することが明確に許可されています。WebLogic JMSでは、非恒久トピック・サブスクリプション用に、JMS 1.1仕様で必須とされているレベルよりも高いレベルのQOSがデフォルトで提供されます。
NO_ACKNOWLEDGEの使用
NO_ACKNOWLEDGE配信モードでは、サーバーからコンシューマにメッセージが送信されますが、確認応答は呼び出されません。かわりに、サーバーはメッセージを事前に確認応答します。この確認応答モードでは、メッセージがすでに確認応答されているため、リカバリを呼び出しても機能しません。このモードを使用すると、確認応答のための追加のネットワーク呼出しによるオーバーヘッドは軽減されますが、サーバー、ネットワーク、またはクライアントで障害が発生した場合にメッセージが消失するおそれがあります。
ノート:
このような状況で非同期クライアントが close()
を呼び出すと、非同期パイプライン内のすべてのメッセージが消失します。
NO_ACKNOWLEDGE
QOSを使用する非同期コンシューマでは、失敗した場合に消失するメッセージの数を抑えるために、メッセージ・パイプラインのサイズを小さくすることが必要になる場合があります。
マルチスレッドの回避
JMSの仕様には、セッション、プロデューサ、コンシューマまたはメッセージ・メソッドをマルチスレッド化すると、close()
を呼び出したときを除いて、定義されていない動作が発生すると記載されています。
http://www.oracle.com/technetwork/java/jms/index.html
に記載されている仕様を参照してください。アプリケーションのスレッドが制限されている場合は、プロデューサおよびセッションの数を増やすことを検討してください。
JMSXUserIDプロパティの使用
WebLogic Server 9.0以降では、メッセージ・センダーの認証されたユーザー名が自動的に伝播されるようにJMS接続ファクトリまたは宛先を構成できます。ユーザー名は、JMSXUserID
というjavax.jms.Message
プロパティに配置されます。
アプリケーションでJMSXUserID
プロパティを使用する場合は、次の点に注意してください。
-
JMSXUserID
プロパティはJMS仕様によって規定されますが、その動作の定義は厳格ではないため、JMSベンダーによって動作が異なる場合があります。 -
JMSXUserID
プロパティは、アプリケーションがJMSプロデューサの作成に使用するスレッドの資格証明に基づいています。JMS送信呼出し時にスレッド上にある資格証明からは派生しません。 -
JMSは、アプリケーションが
JMSXUserID
を直接設定しようとする試行を無視またはオーバーライドします(たとえば、javax.jms.Message.setXXXProperty()
は機能しません)。 -
JMSメッセージは署名または暗号化されません(RMI/EJB呼出しと同様)。したがって、
JMSXUserID
を完全に保護して転送するには、セキュア・プロトコル(t3s
やhttps
など)を使用してメッセージを送信する必要があります。 -
WebLogicストア・アンド・フォワード・エージェントは、
JMSXUserID
を伝播しません(nullに設定します)。 -
WebLogicメッセージング・ブリッジは、メッセージング・ブリッジが9.0以降のJMSサーバーに転送し、
メッセージのプロパティを保持する
ように構成されている場合、ソース宛先のメッセージのJMSXUserIDプロパティを伝播します。それ以外の場合は、転送されたメッセージはユーザー名を含まないか、ブリッジ・センダーによって使用されるユーザー名を含みます。後者の動作は、ブリッジ・センダーの接続ファクトリと宛先の構成によって決定されます。 -
WebLogic JMSの
WLMessageProducer.forward()
拡張を使用すると、受信したメッセージのJMSXUserID
を転送可能です。ノート:
9.0以前のWebLogic JMSクライアントに対する
JMSXUserID
プロパティの相互運用の動作は不明です。
接続ファクトリまたは宛先のJMSXUserID
プロパティの設定手順の詳細は、WebLogic Server管理コンソール・オンライン・ヘルプの次のトピックを参照してください。
パフォーマンスとチューニング
WebLogic JMSで使用可能なパフォーマンス・チューニング機能を実装して、アプリケーションを最大限に活用できます。
『Oracle WebLogic Serverのパフォーマンスのチューニング』のWebLogic JMSのチューニングに関する項を参照してください。