このトピックでは、BizTalk Server ソリューションのパイプラインのパフォーマンスを最適化するためのガイドラインについて説明します。
BizTalk Server パイプラインのパフォーマンスを最適化するためのベスト プラクティス
パイプライン コンポーネントはパフォーマンスに大きな影響を与えるため (たとえば、パススルー パイプライン コンポーネントのパフォーマンスは XML アセンブラー/逆アセンブラー パイプライン コンポーネントよりも最大 30% 向上します)、カスタム パイプライン コンポーネントがデプロイに実装される前に最適に実行されるようにしてください。 BizTalk アプリケーションの全体的なパフォーマンスを最大化する場合は、カスタム パイプライン内のパイプライン コンポーネントの数を最小限に抑えます。
また、パイプライン コンポーネントのメッセージ永続化頻度を減らし、冗長性を最小限に抑えるためにコンポーネントをコーディングすることで、全体的なパフォーマンスを向上させることもできます。 カスタム追跡コンポーネントなど、パフォーマンスを中断する可能性のあるすべてのカスタム アセンブリと特定の成果物は、システムが完全な容量で動作しているときに動作を観察し、考えられるボトルネックを見つけるために、負荷の高い状態で個別にテストする必要があります。
パイプライン コンポーネント内で受信メッセージを読み取る必要がある場合は、 XmlDocument オブジェクトを使用してドキュメント全体をメモリに読み込まないようにします。 XML ドキュメントのメモリ内表現を読み込んで作成するために XmlDocument クラスのインスタンスに必要な領域の量は、実際のメッセージ サイズの最大 10 倍です。 メッセージを読み取るには、 XmlTextReader オブジェクトと次のクラスのインスタンスを使用する必要があります。
VirtualStream (Microsoft.BizTalk.Streaming.dll) - このクラスのソース コードは、SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler と SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm の 2 つの場所にあります。
ReadOnlySeekableStream (Microsoft.BizTalk.Streaming.dll)。
SeekAbleReadOnlyStream - このクラスのソース コードは、次のように Pipelines SDK の下の 2 つの場所にあります。SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler と SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm。
可能な限り、PassThruReceive と PassThruTransmit 標準パイプラインを使用します。 パイプライン コンポーネントが含まれていないため、メッセージの処理は実行されません。 このため、メッセージの受信または送信のパフォーマンスを最大限に高めることができます。 バイナリ メッセージを送信する必要がある場合は、BizTalk MessageBox にバイナリ ドキュメントを発行する必要がある場合は、受信場所で PassThruReceive パイプラインを使用し、送信ポートで PassThruTransmit パイプラインを使用できます。 メッセージが書式設定され、送信する準備ができている場合は、オーケストレーションにバインドされた物理送信ポートで PassThruTransmit パイプラインを使用することもできます。 次のいずれかのアクションを実行する必要がある場合は、別のアプローチを使用する必要があります。
受信 XML またはフラット ファイル メッセージのコンテキストでプロパティを昇格します。
受信場所内にマップを適用します。
メッセージをサブスクライブするオーケストレーションにマップを適用します。
メッセージをサブスクライブする送信ポートにマップを適用します。
これらのアクションのいずれかを実行するには、受信パイプライン内のドキュメントの種類をプローブして検出し、(namespace#root-name) 値を MessageType コンテキスト プロパティに割り当てる必要があります。 この操作は通常、Xml 逆アセンブラー コンポーネント (XmlDasmComp) やフラット ファイル逆アセンブラー コンポーネント (FFDasmComp) などの逆アセンブラー コンポーネントによって実行されます。 この場合は、標準 (XmlReceive パイプラインなど) または標準またはカスタム逆アセンブラー コンポーネントを含むカスタム パイプラインを使用する必要があります。
できるだけ遅くリソースを取得し、できるだけ早く解放します。 たとえば、データベース上のデータにアクセスする必要がある場合は、できるだけ遅く接続を開き、できるだけ早く接続を閉じます。 C# の using ステートメントを使用して、破棄可能なオブジェクトを暗黙的に解放するか、try-catch-finally ステートメントの finally ブロックを使用してオブジェクトを明示的に破棄します。 ソース コードをインストルメント化して、コンポーネントをデバッグしやすくします。
メッセージ処理を高速化するために厳密に必要ではないコンポーネントをパイプラインから排除します。
受信パイプライン内では、メッセージ ルーティング (オーケストレーション、送信ポート) またはメッセージ コンテキスト プロパティ (送信ポート) の降格に必要な場合にのみ、アイテムをメッセージ コンテキストに昇格する必要があります。
メッセージにメタデータを含める必要があり、ルーティングまたは降格の目的でメタデータを使用しない場合は、 IBaseMessageContext.Promote メソッドの代わりに IBaseMessageContext.Write メソッドを使用します。
XPath 式を使用してメッセージから情報を抽出する必要がある場合は、SelectNodes メソッドまたは SelectSingleNode メソッドを使用するためだけに、XmlDocument オブジェクトを使用してドキュメント全体をメモリに読み込まないようにします。 または、「 ストリーミングでのメモリ使用量の最適化」で説明されている手法を使用します。
ストリーミングを使用して、パイプラインでメッセージを読み込むときに必要なメモリ占有領域を最小限に抑える
次の手法では、メッセージをパイプラインに読み込むときにメッセージのメモリ占有領域を最小限に抑える方法について説明します。
ReadOnlySeekableStream と VirtualStream を使用してパイプライン コンポーネントからのメッセージを処理する
パイプライン コンポーネント内のメモリにメッセージ全体を読み込まないようにすることをお勧めします。 望ましい方法は、カスタム ストリーム実装で受信ストリームをラップし、読み取り要求が行われると、カスタム ストリーム実装は基になるラップされたストリームを読み取り、読み取り時に (純粋なストリーミング方式で) データを処理することです。 これは実装が非常に困難であり、ストリームで何を行う必要があるかによっては不可能な場合があります。 この場合は、Microsoft.BizTalk.Streaming.dllによって公開される ReadOnlySeekableStream クラスと VirtualStream クラスを使用します。 これらの実装は、BizTalk SDK の 任意の XPath プロパティ ハンドラー (BizTalk Server サンプル) (https://go.microsoft.com/fwlink/?LinkId=160069) でも提供されています。ReadOnlySeekableStream を使用すると、カーソルをストリームの先頭に移動できます。 VirtualStream は、サイズが指定されたしきい値を超えない限り、内部的に MemoryStream を使用します。この場合、ストリームはファイル システムに書き込まれます。 これらの 2 つのストリームを組み合わせて使用すると (VirtualStream を ReadOnlySeekableStream の永続ストレージとして使用)、"シーク可能性" と "ファイル システムへのオーバーフロー" の両方の機能が提供されます。 これにより、メッセージ全体をメモリに読み込むことなく、大きなメッセージの処理に対応できます。 この機能を実装するために、パイプライン コンポーネントで次のコードを使用できます。
int bufferSize = 0x280;
int thresholdSize = 0x100000;
Stream vStream = new VirtualStream(bufferSize, thresholdSize);
Stream seekStream = new ReadOnlySeekableStream(inboundStream, vStream, bufferSize);
このコードは、各受信場所または送信ポート構成で bufferSize 変数と thresholdSize 変数を公開することで、"オーバーフローしきい値" を提供します。 このオーバーフローしきい値は、開発者または管理者が、メッセージの種類や構成 (32 ビットと 64 ビットなど) ごとに調整できます。
XPathReader と XPathCollection を使用して、カスタム パイプライン コンポーネント内から特定の IBaseMessage オブジェクトを抽出します。
XML ドキュメントから特定の値を取得する必要がある場合は、XmlDocument クラスによって公開される SelectNodes メソッドと SelectSingleNode メソッドを使用する代わりに、次のコード例に示すように、Microsoft.BizTalk.XPathReader.dll アセンブリによって提供される XPathReader クラスのインスタンスを使用します。
注
特に小さいメッセージの場合、SelectNodes または SelectSingleNode で XmlDocument を使用すると、XPathReader を使用するよりもパフォーマンスが向上する場合がありますが、XPathReader を使用すると、アプリケーションのフラット メモリ プロファイルを保持できます。
この例では、XPathReader と XPathCollection を使用して、カスタム パイプライン コンポーネント内から特定 の IBaseMessage オブジェクトを抽出する方法を示します。
public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
try
{
...
IBaseMessageContext messageContext = message.Context;
if (string.IsNullOrEmpty(xPath) && string.IsNullOrEmpty(propertyValue))
{
throw new ArgumentException(...);
}
IBaseMessagePart bodyPart = message.BodyPart;
Stream inboundStream = bodyPart.GetOriginalDataStream();
VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize);
ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(inboundStream, virtualStream, bufferSize);
XmlTextReader xmlTextReader = new XmlTextReader(readOnlySeekableStream);
XPathCollection xPathCollection = new XPathCollection();
XPathReader xPathReader = new XPathReader(xmlTextReader, xPathCollection);
xPathCollection.Add(xPath);
bool ok = false;
while (xPathReader.ReadUntilMatch())
{
if (xPathReader.Match(0) && !ok)
{
propertyValue = xPathReader.ReadString();
messageContext.Promote(propertyName, propertyNamespace, propertyValue);
ok = true;
}
}
readOnlySeekableStream.Position = 0;
bodyPart.Data = readOnlySeekableStream;
}
catch (Exception ex)
{
if (message != null)
{
message.SetErrorInfo(ex);
}
...
throw ex;
}
return message;
}
XMLReader と XMLWriter と XMLTranslatorStream を使用してパイプライン コンポーネントからのメッセージを処理する
ストリーミング アプローチを使用するカスタム パイプライン コンポーネントを実装するもう 1 つの方法は、BizTalk Server によって提供される XmlTranslatorStream クラスと組み合わせて .NET XmlReader クラスと XmlWriter クラスを使用することです。 たとえば、Microsoft.BizTalk.Pipeline.Components アセンブリに含まれる NamespaceTranslatorStream クラスは XmlTranslatorStream から継承され、古い名前空間をストリームに含まれる XML ドキュメント内の新しい名前空間に置き換えるために使用できます。 カスタム パイプライン コンポーネント内でこの機能を使用するには、メッセージ本文部分の元のデータ ストリームを NamespaceTranslatorStream クラスの新しいインスタンスでラップし、後者を返します。 この方法では、受信メッセージはパイプライン コンポーネント内で読み取られたり処理されたりすることはありません。ただし、ストリームが同じパイプライン内の後続のコンポーネントによって読み取られた場合、または最終的にメッセージ エージェントによって使用されてから BizTalk Server メッセージ ボックスにドキュメントが発行される場合に限られます。
次の例は、この機能の使用方法を示しています。
public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
IBaseMessage outboundMessage = message;
try
{
if (context == null)
{
throw new ArgumentException(Resources.ContextIsNullMessage);
}
if (message == null)
{
throw new ArgumentException(Resources.InboundMessageIsNullMessage);
}
IBaseMessagePart bodyPart = message.BodyPart;
Stream stream = new NamespaceTranslatorStream(context,
bodyPart.GetOriginalDataStream(),
oldNamespace,
newNamespace);
context.ResourceTracker.AddResource(stream);
bodyPart.Data = stream;
}
catch (Exception ex)
{
if (message != null)
{
message.SetErrorInfo(ex);
}
throw ex;
}
return outboundMessage;
}
カスタム パイプライン コンポーネントでの ResourceTracker の使用
パイプライン コンポーネントは、作成するオブジェクトの有効期間を管理し、これらのオブジェクトが不要になったらすぐにガベージ コレクションを実行する必要があります。 パイプライン コンポーネントがオブジェクトの有効期間をパイプライン実行の最後まで続ける場合は、パイプラインがパイプライン コンテキストからフェッチできるそのようなオブジェクトをリソース トラッカーに追加する必要があります。
リソース トラッカーは、次の種類のオブジェクトに使用されます。
ストリームオブジェクト
COM オブジェクト
IDisposable オブジェクト
メッセージ エンジンは、リソース トラッカーに追加されたすべてのネイティブ リソースが、成功したか失敗したかに関係なく、パイプラインが完全に実行された後の適切なタイミングで解放されるようにします。 Resource Tracker インスタンスと追跡しているオブジェクトの有効期間は、パイプライン コンテキスト オブジェクトによって管理されます。 パイプライン コンテキストは、IPipelineContext インターフェイスを実装するオブジェクトを介して、すべての種類のパイプライン コンポーネントで使用できます。
たとえば、次のコード スニペットは、カスタム パイプライン コンポーネントで ResourceTracker プロパティを使用する方法を示すサンプルです。 ResourceTracker プロパティを使用するには、コード スニペットで次のパラメーター
IPipelineContext.ResourceTracker.AddResource
を使用します。 このパラメーターでは、次の操作を行います。IPipelineContext インターフェイスは、すべてのドキュメント処理固有のインターフェイスにアクセスするために使用されるメソッドを定義します。
ResourceTracker プロパティは IPipelineContext を参照し、パイプライン処理の最後に明示的に破棄されるオブジェクトを追跡するために使用されます。
ResourceTracker.AddResource メソッドは、COM オブジェクト、破棄可能オブジェクト、ストリームを追跡するために使用され、メッセージが BizTalk MessageBox に発行されるときに、カスタム パイプライン コンポーネント内で常に使用して、これらの種類のリソースを明示的に閉じる (ストリーム)、破棄 (IDisposable オブジェクト) または解放 (COM オブジェクト) する必要があります。
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
IBaseMessage outMessage = pContext.GetMessageFactory().CreateMessage();
IBaseMessagePart outMsgBodyPart = pContext.GetMessageFactory().CreateMessagePart();
outMsgBodyPart.Charset = Encoding.UTF8.WebName;
outMsgBodyPart.ContentType = "text/xml";
//Code to load message content in the MemoryStream object//
MemoryStream messageData = new MemoryStream();
//The MemoryStream needs to be closed after the whole pipeline has executed, thus adding it into ResourceTracker//
pContext.ResourceTracker.AddResource(messageData);
//Custom pipeline code to load message data into xmlPayload//
XmlDocument xmlPayLoad = new XmlDocument();
xmlPayLoad.Save(messageData);
messageData.Seek(0, SeekOrigin.Begin);
//The new stream is assigned to the message part’s data//
outMsgBodyPart.Data = messageData;
// Pipeline component logic here//
return outMessage;
}
インメモリ アプローチとストリーミング アプローチを使用したパイプラインへのメッセージの読み込みの比較
次の情報は、Nic Barden のブログ、 http://blogs.objectsharp.com/cs/blogs/nbarden/archive/2008/04/14/developing-streaming-pipeline-components-part-1.aspx (https://go.microsoft.com/fwlink/?LinkId=160228) から取得しました。 次の表は、インメモリ アプローチとストリーミング アプローチを使用したパイプラインへのメッセージの読み込みの概要を示しています。
比較... | ストリーミング | メモリ内 |
---|---|---|
メッセージあたりのメモリ使用量 | 低、メッセージ サイズに関係なく | 高 (メッセージ サイズによって異なります) |
XML データの処理に使用される一般的なクラス | 以下の組み込み派生とカスタム派生: XmlTranslatorStream、XmlReader、XmlWriter |
XmlDocument、XPathDocument、MemoryStream、VirtualStream |
ドキュメンテーション | Poor – サポートされていないBizTalkクラスと、文書化されていないBizTalkクラスが多い | 非常に良い - .NET Framework クラス |
"処理ロジック" コードの場所 | - Execute メソッドを使用してリーダーとストリームを "ワイヤアップ" します。 - 実際の実行は、データの読み取り時にリーダーとストリームで行われます。 |
パイプライン コンポーネントの Execute メソッドから直接。 |
データ | データがそれを通して読み取られると、各ラッピングレイヤーで再作成されます。 | 次のコンポーネントが呼び出される前に、各コンポーネントで読み取り、変更、書き込みを行います。 |