ストリーミングによるメモリ使用量の最適化
このトピックでは、WCF トランスポートを使用して大きなメッセージを送受信するとき、またはオーケストレーションでメッセージを読み込むときに、ストリーミング パターンを使用してメッセージ のメモリ占有領域を最小限に抑えるための推奨事項について説明します。
オーケストレーションでコードを使用してメッセージの内容を読み取る場合は、XmlDocument 変数を使用しないでください。 XmlDocument 変数にメッセージを読み込むと、特に大きなメッセージに対して大きなオーバーヘッドが発生します。 このオーバーヘッドは、メモリ内構造を構築するためのメモリ使用量と処理に関するものです。 XmlDocument インスタンスを使用すると、ドキュメント オブジェクト モジュール (DOM) のオブジェクト グラフを作成するために、メッセージの内容全体が強制的にメモリに読み込まれます。 このクラスのインスタンスによって使用されるメモリの合計量は、実際のメッセージ サイズの約 10 倍になります。 XmlDocument 変数にメッセージを読み込むときに必要なメモリ占有領域の詳細については、 第 9 章 - XML パフォーマンスの向上に関するページを参照してください。
このトピックの残りの部分では、XmlDocument 変数にメッセージを読み込む必要のないメッセージの内容を読み取るための別の方法を提供します。
WCF トランスポートで大きなメッセージを送受信するときにストリーミングを使用する
WCF トランスポートで大きなメッセージを送受信する場合は、WCF-Custom または WCF-CustomIsolated アダプターを使用し、 transferMode = Streamed オプションをサポートするバインドの種類 (次のバインドなど) を使用してを構成します。
basicHttpBinding + BasicHttpBindingElement、transferMode = Streamed
netTcpBinding + NetTcpBindingElement、transferMode = Streamed
customBinding + HttpTransportElement、transferMode = Streamed
customBinding +ConnectionOrientedTransportElement、transferMode = Streamed
transferMode = Streamed オプションをサポートするバインドと共に WCF-Custom アダプターまたは WCF-CustomIsolated アダプターを選択すると、必要に応じてファイル システムへの大きなメッセージのストリーミングが実装され、メモリ不足の潜在的な問題が軽減されます。
ストリーミングを使用して、オーケストレーションでメッセージを読み込むときに必要なメモリ占有領域を最小限に抑える
次の手法では、オーケストレーションにメッセージを読み込むときに、メッセージのメモリ占有領域を最小限に抑える方法について説明します。
XLANGMessage 変数を使用して、メッセージまたはメッセージ部分の内容を処理する
オーケストレーションから .NET クラス ライブラリにメッセージを渡す場合は、このトピックで前述した理由により、メッセージを XmlDocument 変数として渡さないでください。代わりに XLANGMessage 変数を使用してください。 次の手法は、XLANGMessage 変数を使用してメッセージまたはメッセージ部分を読み取るためのメソッドを示しています。
XMLReader を使用してメッセージを処理 する - XmlReader インスタンスでメッセージを処理するには、メッセージを XLANGMessage として .NET コードに渡し、XmlReader を使用して Part コンテンツを取得します。
public void ProcessMessage(XLANGMessage message) { try { using (XmlReader reader = message[0].RetrieveAs(typeof(XmlReader)) as XmlReader) if (reader != null) { ... } } finally { message.Dispose(); } }
StreamReader を使用してメッセージの内容を文字列に取得 する - オーケストレーションでの XmlDocument の一般的な用途の 1 つは、XmlDocument.OuterXml() を使用して XML 文字列としてメッセージを取得することです。 次のコード例は、XLANGMessage 変数を使用してメッセージを文字列として取得する別のメソッドを示しています。
public static string MessageToString(XLANGMessage message) { string strResults; try { using (Stream stream = message[0].RetrieveAs(typeof(Stream)) as Stream) { using (StreamReader reader = new StreamReader(stream)) { strResults = reader.ReadToEnd(); } } } finally { message.Dispose(); } return strResults; }
単純な .NET メッセージの内容を文字列に取得 する - メッセージの型が単純な .NET 型の場合は、その型としてメッセージを取得できます。 たとえば、メッセージを文字列として取得するには、メッセージを XLANGMessage として .NET コードに渡し、Part コンテンツを文字列として取得します。
public void ProcessMessage(XLANGMessage message) { try { string content = message[0].RetrieveAs(typeof(string)) as string; if (!string.IsNullOrEmpty(content)) { ... } } finally { message.Dispose(); } }
メッセージの内容をストリームに取得 する - メッセージをストリームとして取得するには、メッセージを XLANGMessage として .NET コードに渡し、Part コンテンツをストリームとして取得します。
public Stream ProcessRequestReturnStream(XLANGMessage message, int bufferSize, int thresholdSize) { ... try { using (VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize)) { using (Stream partStream = (Stream)message[0].RetrieveAs(typeof(Stream))) //Note that when calling this code, if the XmlDocument is quite large, keeping it in a memory with a MemoryStream may have an adverse effect on performance. //In this case, it may be worthwhile to consider an approach that uses a VirtualStream + ReadonlySeekableStream to buffer it to the file system, if its size is bigger than the thresholdSize parameter. //Keep in mind that: // - If the message size is smaller than the threshold size, the VirtualStream class buffers the stream to a MemoryStream. // - If the message size is bigger than the threshold size, the VirtualStream class buffers the stream to a temporary file. using (ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(partStream, virtualStream, bufferSize)) { using (XmlReader reader = XmlReader.Create(readOnlySeekableStream)) { } } } } } catch (Exception ex) { } finally { message.Dispose(); } return stream; }
メッセージの内容を .NET オブジェクトに取得 する - メッセージを .NET オブジェクトとして取得するには、メッセージを .NET コードに XLANGMessage として渡し、Part コンテンツを .NET クラスのインスタンスとして取得します。 Visual Studio 2010 で提供される XML スキーマ定義ツール (Xsd.exe) ツールを使用して、メッセージの Xml スキーマからこれを作成します。
Note
この手法は、メッセージが小さい場合にのみ有効です。 それ以外の場合、この方法では、実際のメッセージを .NET オブジェクトにシリアル化解除するための大きなオーバーヘッドが発生する可能性があります。
public void ProcessMessage(XLANGMessage message) { try { Request request = message[0].RetrieveAs(typeof(Request)) as Request; if (request != null) { ... } } finally { message.Dispose(); } }
Note
.NET コードから返す前に XLANGMessage パラメーターによって公開される Dispose() メソッドの使用は、時間の経過と同時に解放することなく XLANGMessage オブジェクトのインスタンスを蓄積できる、ループ シナリオや実行時間の長いオーケストレーションで特に重要です。 メッセージの呼び出しについて詳しくは、以下をご覧ください。この方法で Dispose() を使用する場合は、BizTalk Serverドキュメントの「XLANGMessage として表されるメッセージ」を参照してください。 このトピックでは、IStreamFactory を使用して、ストリーム ベースのアプローチを使用してユーザー コードで XLANGMessage 変数を構築するためのベスト プラクティスについても説明します。
オーケストレーションによって呼び出されるヘルパー コンポーネント内で XLANGMessage を処理するさまざまな方法の詳細については、次のトピックを参照してください。
4 オーケストレーションによって呼び出されたヘルパー コンポーネント内で XLANGMessage を処理するさまざまな方法パート 1 (サード パーティブログを開く)
4 オーケストレーションによって呼び出されたヘルパー コンポーネント内で XLANGMessage を処理するさまざまな方法パート 2 (サード パーティブログを開く)
XPathReader と XPathCollection を使用して、オーケストレーションによって呼び出されたメソッドから XLANGMessage オブジェクトから値を抽出する
XMLDocument クラスを使用して、カスタム パイプライン コンポーネントやオーケストレーションによって呼び出されるヘルパー クラスなど、カスタム コードから XML メッセージの内容を読み取らないようにします。 XMLDocument インスタンスを使用して XML メッセージを読み込む場合、メッセージ全体がメモリに読み込まれます。これは非効率的であり、メッセージの実際のサイズの最大 10 倍のメモリが必要になる場合があります。 XML メッセージの内容をより効率的に読み取る方法は、ストリーミング手法を使用して、Microsoft.BizTalk.Streaming.dll アセンブリによって提供されるストリーム クラスのいずれかで元のストリームをラップすることです。 この手法は、大きなメッセージを読み込む場合に特に便利です。
XmlDocument クラスによって公開される SelectNodes メソッドと SelectSingleNode メソッドを使用する代わりに、XML ドキュメントから特定の値を取得する必要がある場合は、次のコード例に示すように、Microsoft.BizTalk.XPathReader.dll アセンブリによって提供される XPathReader クラスのインスタンスを使用します。
この例では、XPathReader と XPathCollection を使用して、オーケストレーションによって呼び出されたメソッド内の XLANGMessage オブジェクトから特定の値を抽出する方法を示します。
public static string SelectSingleNode(XLANGMessage message, string xPath) { try { if (message == null || string.IsNullOrEmpty(xPath)) { return string.Empty; } using (XmlReader reader = (XmlReader)message[0].RetrieveAs(typeof(XmlReader))) { XPathCollection xPathCollection = new XPathCollection(); XPathReader xPathReader = new XPathReader(reader, xPathCollection); xPathCollection.Add(xPath); while (xPathReader.ReadUntilMatch()) { if (xPathReader.Match(0)) { return xPathReader.ReadString(); } } } } catch (Exception ex) { ... } finally { message.Dispose(); } return string.Empty; }