新しい IBM Developer JP サイトへようこそ!サイトのデザインが一新され、旧 developerWorks のコンテンツも統合されました。 詳細はこちら

MQ虎の巻 第4回「アプリケーション設計」

1

アプリケーション設計について

前回の「適用業務のパターン化」では、受注業務を例に取り、実際の業務をMQの処理パターンに分類していく過程について解説しました。それを受けて、今回はMQのアプリケーションを設計していく上での、ガイドラインについて解説していきます。

まずは、MQアプリケーションの典型的な構造について解説します。次に前回に紹介したMQの処理パターンについて、アプリケーション設計の段階におけるガイドライン、考慮点やヒントを述べます。最後に、機能要件とMQパラメーター設定との対照表を挙げます。

2

MQアプリケーションの構造

一般に、代表的なMQアプリケーションは左図のような構造を持ちます。

alt

パフォーマンスの観点から、MQCONN、MQOPEN、MQCLOSE、MQDISCといった、アプリケーション内で一度だけ実行すれば十分であるMQIは初期化処理又は終了処理に含めます。メインロジックは通常ループ処理となるため、この中にこれらのMQIを含める事は避けます。

以下に、各処理段階における考慮点について述べます。

(1) 初期化処理

  • プログラムの起動法を選択する必要があります。メッセージ到着時にMQのトリガー機能によって起動する場合と、コマンドラインから予め起動しておく常駐型の場合の二種に大別されます。どちらの起動方法を選択するかは、そのアプリケーションの特性によります。一般に常駐型はパフォーマンスの観点で優れているため、高メッセージレートの処理で用いられる事が多く、反面、トリガー起動の場合は運用が単純になるという利点があります。
  • 最初に述べたとおり、MQCONNとMQOPENは基本的にメインロジックのループ内には入れずに、初期化処理にて実行しておきます。ただし、アクセスするキューを動的に決定するようなアプリケーションの場合は、MQPUT1を使用するか、メインロジック内でMQOPENを行うことになります。メインロジックのループ内でMQOPENを実行する場合も、MQOPENの結果得られるハンドルを保持しておき、再度同じキューにアクセスする必要が出た場合にはそのハンドルを再利用すれば、パフォーマンスに優れたアプリケーションとなります。
  • バッファーの確保、DBMSのような他のミドルウェアの初期化処理もこの段階で行っておきます。

(2) メインロジック

  • MQGET/MQPUT/MQPUT1や、DB・ファイルの参照・更新により、業務処理を行います。詳細に関しては次節で述べます。
  • MQBEGIN/MQCMIT/MQBACKにより、同期点を管理します。MQ以外のコンポーネントがトランザクション・コーディネーターの場合は、そのコンポーネントが提供するAPIにより同期点を管理することになります。
  • データベース管理システムとの連携: アプリケーションにDB処理が含まれる場合は、MQ資源とDB資源の更新に対する同期を取る必要があります。キューマネージャーがトランザクション・コーディネーターとなって2フェーズコミットを行う方法と、リソースマネージャーとしてトランザクションに参加する方法があります。
  • 常駐型のアプリケーションはMQGETでメッセージの到着を待っているのが一般的で、放っておくと何時までもメッセージを待ち続けるような設計になっています。したがって、常駐型のアプリケーションを終了させるために何らかの方法を実装しておく必要があります。一般的な終了方法としては以下のものが挙げられます。

1) MQGMO_FAIL_IF_QUIESCINGを指定

24時間連続稼動するような業務で、アプリケーション毎を個別に止める要件が無い場合には、キューマネージャーの停止時に自動的にMQGET状態から抜けるようにしておきます。この際には”MQRC_Q_MGR_QUIESCING”という戻りコードがアプリケーションに返されます。これを受けて終了処理を開始します。

2) コマンドでキューをGET(DISABLED)にする

MQGETでアプリケーションがメッセージの到着を待っている時に”ALTER QLOCAL GET(DISABLED)”コマンドが対象のキューに実行されると、アプリケーションにはMQRC_GET_INHIBITEDが返り、MQGETから抜けます。これを受けて終了処理を開始します。

3) アプリケーションで取り決めた終了メッセージをキューに書きこむ

特定のタイプのメッセージをMQGETした場合にはアプリケーションが終了処理を実行するように設計します。MQMDのFormatフィールドを利用するとよいでしょう。

(3) 終了処理

  • MQDISCやMQCLOSEを実行します。
  • 他のミドルウェアの終了処理や、初期化処理時に確保したバッファーの解放も処理もここで行います。
3

処理パターン別アプリケーション設計考慮点

前回の連載で、MQの代表的な処理パターンとして、下記の4つを挙げました。

(1) オンライン処理: 一方向型 (2) オンライン処理: 双方向型(要求/応答型) (3) バッチ転送処理 (4) ディレード処理

この節ではこれらの処理パターンをMQアプリケーションとして設計するときのガイドラインや考慮点について解説していきます。

(1) オンライン処理: 一方向型

alt

このパターンはMQアプリケーションの最も単純な形態です。クライアント・アプリケーションはMQPUTによりメッセージを送信し、サーバー・アプリケーションはMQGETでメッセージを受信します。

それぞれのアプリケーションのMQIで留意すべき点を以下にあげます。

  • クライアント・アプリケーション
    1. 確実な転送を保証するため、通常メッセージの永続性はパーシステントを選択します。
    2. パフォーマンスの理由等からパーシステントを選択できいない場合には、フロントエンド・システム上に再送可能な元データを保持しておく必要があります。
    3. バックエンドに送信したメッセージが何らかの理由で正常に処理されないケースに対応する方法として、レポートメッセージの使用を検討します(パラメーター設定ガイドラインの8)。
  • サーバーアプリケーション
    1. 2フェーズコミットがサポートされていない資源を更新する際は二重処理防止を検討します(パラメーター設定ガイドラインの3)。

(2) オンライン処理: 双方向型(要求/応答型)

alt

このパターンはオンライン業務で一般に使用される形態です。受付アプリケーションはMQPUT+MQGETというパターンでMQIを実行し、サーバーアプリケーションはMQGET+MQPUTというパターンでMQIを実行します。通常、このサーバーアプリケーションは常駐型で、MQGET+MQPUTをループ処理します。

それぞれのアプリケーションのMQIで留意すべき点を以下にあげます。

  • 受付アプリケーション
    1. このアプリケーションは複数のフロントシステム上で並行稼動する事が想定されるため、サーバーアプリケーションの応答が間違いなく要求を出したアプリケーションに戻るようにしなければなりません(パラメーター設定ガイドラインの2と10)。
    2. 通常即応性が求められるため、タイムアウトを設定します(パラメーター設定ガイドラインの7)。
    3. メッセージの永続性を選択します。
  • サーバーアプリケーション
    1. 2フェーズコミットがサポートされていない資源を更新する際は二重処理防止を検討します(パラメーター設定ガイドラインの3)。
    2. MQGETしたメッセージのMQMDから必要なフィールドをMQPUTするメッセージのMQMDにコピーします(パラメーター設定ガイドラインの2、7、10)。この形態のアプリケーションは照会処理か更新処理かで、MQIのパラメーター設定値の考え方が変わります。
  • 照会処理の場合には、メッセージの永続性はノンパーシステントを選択し、MQPUTとMQGETはNO_SYNCPOINTオプションにて実行します。クライアント・アプリケーションもサーバー・アプリケーション側もNO_SYNCPOINTオプションで構いません。
  • 更新処理の場合には、原則的にSYNCPOINTオプションを使用してMQPUTやMQGETを実行します。ただし、クライアントアプリケーションで他の資源の更新が無い場合や、サーバーアプリケーションで更新する資源が2フェーズコミットの対象外の場合などはNO_SYNCPOINTオプションを選択するケースもあります。永続性もパーシステントの場合とノンパーシステントの場合が考えられます。前者の場合、即応性を求められる業務には向かない可能性があります。後者の場合だと、更新が完了したかどうかの問合せ、又は、更新処理取り消しのためのメッセージをクライアントアプリケーションから送信する機能が必要になります。

(3) バッチ転送処理

alt

上の例は、複数のシステムで生成されたバッチ入力データを、1システムに集約して処理する形態を想定しています。バッチ処理は様々な形態が考えられるため、上の図は一例としてとらえて下さい。この場合、MQの処理層では、即応性は求められないものの確実な転送が求められます。受付アプリケーションはMQPUTによりメッセージを送信し、サーバーアプリケーションはMQGETによりメッセージを受信する形態を取ります。

それぞれのアプリケーションのMQIで留意すべき点を以下にあげます。

  • バッチ入力アプリケーション
    1. バッチデータとは別に、バッチ単位毎に制御メッセージも送信します。ここには、バッチ単位がいくつのメッセージからなるかを記述し、入力データの送信が完了してから、転送先システムに向けて送信します。
    2. バッチ入力データは通番して欠番や重複がすぐわかるようにしておく事が望ましいです。
    3. 確実な転送を保証するため、通常メッセージの永続性はパーシステントを選択します。
    4. バックエンドに送信したメッセージがサーバーアプリケーションで処理されたかどうかの確認が必要な場合は、レポートメッセージの使用を検討します(パラメーター設定ガイドラインの8)。
    5. サーバー側に複数のメッセージをまとめて処理させたい場合はグループ化を検討する(パラメーター設定ガイドラインの5)。
    6. 入力データサイズが巨大な時は、複数のメッセージに分割して送信する事を検討する(パラメーター設定ガイドラインの6)。
  • サーバーアプリケーション
    1. サーバーアプリケーションは制御キューに対してWait付きのMQGETを実行します。制御キューからメッセージを受信したら、関連するバッチ入力データをバッチキューから取り出します。
    2. 2フェーズコミットがサポートされていない資源を更新する際は二重処理防止を検討します(パラメーター設定ガイドラインの3)。
    3. 受信するメッセージのサイズが不定の場合、適当なサイズのバッファーを確保しておき、それでは足りない場合だけ、大きなバッファーを割り当てるようにします(パラメーター設定ガイドラインの4)。

(4) ディレード処理

alt

このタイプのアプリケーションは、フロントエンド・システム内の処理のみの完了時点で端末にOKを返した後に、実際の業務処理をバックエンド・システムで行うことになります。したがって、MQの処理層では、即応性は求められないものの確実な転送が求められます。受付アプリケーションはMQPUTによりメッセージを送信し、サーバーアプリケーションはMQGETによりメッセージを受信します。

それぞれのアプリケーションのMQIで留意すべき点を以下にあげます。

  • 受付アプリケーション
    1. 確実な転送を保証するため、通常メッセージの永続性はパーシステントを選択します。
    2. バックエンドに送信したメッセージがサーバーアプリケーションで処理されたかどうかの確認が必要な場合は、レポートメッセージの使用を検討します(パラメーター設定ガイドラインの8)。
  • サーバーアプリケーション
    1. 2フェーズコミットがサポートされていない資源を更新する際は二重処理防止を検討します(パラメーター設定ガイドラインの3)。
4

パラメーター設定ガイドライン

MQIには多くのパラメーターが用意されていますが、単純なMQPUT/MQGETを行うだけであれば、ほとんどのものがデフォルト値で構いません。実際には各アプリケーションがどのような機能を備えるべきかという機能要件に従って、様々なパラメーターを適切に設定する事が設計時に必要とされます。下記の表は、アプリケーションの機能要件と使用すべきパラメーターの関係をまとめたものです。各パラメーターの詳細については「アプリケーション・プログラミング・リファレンス」マニュアルを参考にして下さい。

  機能要件 使用するパラメーター 概要
1 キューマネージャーが停止する際、仕掛かり中のMQIを即時終了させる MQOO.Options,
MQPMO.Options,
MQGMO.Options
MQOPEN,MQPUT,MQGET時にそれぞれ、

MQOO_FAIL_IF_QUIESCING
MQPMO_FAIL_IF_QUIESCING
MQGMO_FAIL_IF_QUIESCING

を指定するとキューマネージャーの停止時にMQIが即時終了し、アプリケーションにはエラーが返ります。これらのパラメーターを指定しておかないと、キューマネージャーはMQIが完了するまで、停止できなくなります。したがって、これらのパラメーターは常に設定しておくべきです。
2 要求メッセージと応答メッセージの関連付け MQMD.MsgId,
MQMD.CorrelId
クライアントがMQPUTした際に出力されるMsgIdをCorrelIdフィールドにセットする事により、マッチングを行います
3 二重処理の防止 1)MQMD.BackoutCount
2)MQGMO.Options
アプリケーションが2フェーズコミットをサポートしていないリソースを更新する場合、二重処理を防止するようにアプリケーションを設計する必要があります

1)MQGET後、MQMD.BackoutCountが1以上である場合、処理を中断し、そのメッセージは退避するか、クライアント側にエラーを返すようにします

2)MQGET時のMQGMOにMQGMO_NO_SYNCPOINTを指定し、アプリケーションにエラーが起こってもメッセージをバックアウトさせないようにします
4 用意したバッファーサイズを超えるメッセージを受信した際の対処 DataLength,Reason
(MQGETの引数)
MQGET時に指定したBufferLengthより実際のメッセージ長のほうが大きい場合、MQGETは失敗し、2080がReasonに、実際のメッセージ長がDataLengthにセットされます。DataLengthの値を基に、再度バッファーを確保し、同一メッセージを正常にMQGETで取り出す事が可能になります。
ただし、最初のMQGET時にMQGMO_ACCEPT_TRUNCATED_MSGをMQGMOに指定すると、バッファーサイズ分のメッセージのみ取り出して、MQGETは完了してしまうので注意が必要です。
5 複数のメッセージをグループ化して送受信する MQPMO.Options,
MQMD.MsgFlags,
MQGMO.Options,
MQGMO.GroupStatus
【送信時】
MQPMO.OptionsにMQPMO_LOGICAL_ORDERをMQMD.MsgFlagsにMQMF_MSG_IN_GROUPを指定しMQPUTを行います。最後のメッセージのみ、MQMD.MsgFlagsにMQMF_LAST_MSG_IN_GROUPを指定します

【受信時】
MQGMO.OptionsにMQGMO_LOGICAL_ORDERを指定し、MQGMO.GroupStatusがMQGS_LAST_MSG_IN_GROUPになるまで、MQGETを繰り返します。
6 サイズの大きいメッセージを、複数のサイズの小さなメッセージに分割して送受信する MQPMO.Options,
MQMD.MsgFlags,
MQGMO.Options,
MQGMO.SegmentStatus
【送信時】
MQPMO.OptionsにMQPMO_LOGICAL_ORDERをMQMD.MsgFlagsにMQMF_SEGMENTを指定しMQPUTを行う。最後のメッセージのみ、MQMD.MsgFlagsにMQMF_LAST_SEGMENTを指定します

【受信時】
MQGMO.OptionsにMQGMO_LOGICAL_ORDERを指定し、MQGMO.SegmentStatusがMQSS_LAST_SEGMENTになるまで、MQGETを繰り返します。
7 タイムアウトを設定する MQGMO.Options
MQGMO.WaitInterval
MQMD.Expiry
クライアントプログラムはMQPUTで要求メッセージをサーバーに送信した後、MQGMO.OptionsにMQGMO_WAITをセットし、MQGMO.WaitIntervalにタイムアウト値をセットします。
また、クライアントプログラムがMQPUTで要求メッセージを送信する際、タイムアウト値をMQMD.Expiryにセットしておく事で、クライアントプログラムがタイムアウトしてしまった後に応答メッセージがキューに到着し、ゴミとして残ってしまうという事象を回避できます。
8 宛先にメッセージが到着したことを確認する(レポートメッセージの使用) MQMD.Report
MQMD.ReplyToQ
MQMD.ReplyToQMgr
MQPUTする際にMQMD.ReportにMQRO_EXCEPTION等のレポートオプションを指定します
9 メッセージに優先順位をつける MQMD.Priority MMQPUTの際にMQMD.Priorityに0~9(最高)の優先順位を付ける事が可能です。メッセージがキューに格納される時点で優先順位が評価されて、優先順位が高いものは低いものより先に取り出されるように格納されます。優先順位が同じメッセージはFIFOで扱われます
10 クライアントプログラムが指定した宛先にサーバープログラムが応答メッセージを返す MQMD.ReplyToQ,
MQMD.ReplyToQMgr
MQOD.ObjectName
MQOD.ObjectQMgrName
要求応答型のアプリケーションで、要求メッセージを送信するクライアントアプリケーションが、応答メッセージの戻り先のキュー名とキューマネージャー名をMQMDに指定します(それぞれ、MQMD.ReplyQとMQMD.ReplyToQMgr)。サーバーアプリケーションはこれらの値をそれぞれMQOD.ObjectNameとMQOD.ObjectQMgrNameにコピーし、MQPUT1又はMQOPENを実行し、応答メッセージを送信します。