16

wsHttpを使用して WCF を使用して( NetDataContractSerializer経由で) 大量のシリアル化されたオブジェクト グラフを転送する必要がある場合があります。メッセージ セキュリティを使用しており、引き続き使用したいと考えています。このセットアップを使用して、シリアライズされたオブジェクト グラフを転送したいと思います。転送しようとすると、System.InsufficientMemoryException 型の例外が表示されるようになりました。

少し調査した結果、WCF では、デフォルトで、サービス呼び出しの結果が、シリアル化されたデータを含む単一のメッセージ内に含まれており、このデータは、メッセージ全体が完全に書き込まれるまでデフォルトでサーバーにバッファリングされるようです。したがって、メモリ例外は、サーバーがバッファがいっぱいであるために割り当てることができるメモリ リソースを使い果たしているという事実によって引き起こされています。私が遭遇した 2 つの主な推奨事項は、ストリーミングまたはチャンクを使用してこの問題を解決することですが、それが何を伴うのか、また現在のセットアップ (wsHttp/NetDataContractSerializer/Message Security) でどちらの解決策も可能かどうかは明確ではありません。これまでのところ、メッセージの暗号化と復号化は部分的なメッセージではなく、データのセット全体に対して機能する必要があるため、ストリーミング メッセージ セキュリティを使用しても機能しないことを理解しています。チャンクは可能かもしれませんが、私がリストした他の制約でどのように行われるかは明確ではありません。利用可能なソリューションとその実装方法について誰かがガイダンスを提供できれば、非常に感謝しています。

私の場合、通信の両側を所有および制御し、どちらの側にも転送されるデータに共有インターフェイス パターンを使用するため、他のクライアントとの相互運用性については特に心配していません。したがって、私は、wsHttp をメッセージ セキュリティと共に使用して、NetDataContractSerializer を使用してシリアル化されたオブジェクト グラフを転送するという制約の範囲内に収まるアイデアを受け入れており、既存のサービスと周囲のインフラストラクチャを大幅に変更する必要がないソリューションを好みます。

関連リソース:

このデータに対して実行できるあらゆるタイプの圧縮にも興味がありますが、クライアントが gzip を自動的にサポートするように、.NET 4.0 に移行できるようになったら、トランスポート レベルでこれを行うのがおそらく最善のようです。これを正しく理解していればヘッダー。

更新 (2010-06-29):

バッファリングされたメッセージが大きすぎることが問題の原因であるという結論に達した方法に関するいくつかの歴史。もともと、テスト中に以下のCommunicationExceptionを見ました。

基になる接続が閉じられました: 接続が予期せず閉じられました。

最終的に、これを実行してさらにログを記録した後、指定されたメッセージで問題を引き起こしている基になるInsufficientMemoryException例外を見つけました。

268435456 バイトのマネージ メモリ バッファの割り当てに失敗しました。使用可能なメモリ量が少ない可能性があります。

これは、次の方法に由来します。

System.ServiceModel.Diagnostics.Utility.AllocateByteArray(Int32 サイズ)

つまり、失敗は配列の割り当てに起因します。シリアル化された同じデータをディスクに書き込むと、約 146MB を占めます。それを半分に減らすと、エラーが発生しなくなりますが、バッファを壊す特定のしきい値と、それがシステムに固有なのか、それともいいえ。

更新 (2010 年 12 月 6 日):

この時点で、次の説明を求めていると思います。私の理解では、デフォルトではメッセージセキュリティを備えたWCF wsHttpでは、応答がクライアントに送り返される前にメッセージ全体(通常は返されるデータセット全体)をサーバーにバッファリングする必要があり、問題が発生します。

可能な解決策:

  • データ サイズの制約 - 送信バッファの最大容量を消費しないようにするために、なんらかの形式の圧縮、エンコード、またはメソッドのようなページングを使用して返される実際のデータの制限を使用します。
  • ストリーミング - WCF を介してストリーミング方式で大量のデータを送信できますが、これらの手法ではすべてのデータをバッファリングする必要があるため、これは wsHttp または MessageSecurity と互換性がありません。
  • Chunking Channel - データを個別のメッセージに分割できるようにしますが、現時点では、これがサービス コントラクトの設計に及ぼす制約と、メッセージ バインディングで wsHttp を引き続き使用できるかどうかはわかりません。

返すことができるデータを制限することは、ある程度までしか機能せず、ストリーミング オプションと同様に、これらのオプションでは、WCF サービス呼び出しの外部で多くの下位レベルの作業をコーディングする必要があります。したがって、私が知る必要があるのは、単一のデータ セットをサーバー上で個別のメッセージに分割し、クライアント上でつなぎ合わせることができるようにすることで、大きなメッセージの問題を回避できる可能性のあるチャンキング チャネルの実装があるかどうかということです。これにより、既存のサービス コントラクトのインターフェイス/形状を変更する必要がなくなり、メッセージ セキュリティと wsHttp を使用しながら、各サービス実装のクライアントとサーバーの部分からプロセスがほとんど隠されます。チャンキング チャネルで、ストリームを公開するためにサービス コントラクトを書き直す必要がある場合は、必要ありません。これがストリーミング ソリューションと実際にどのように異なっているかがわかります。誰かが私のためにこれらの質問に簡単に答えることができれば、賞金を授与し、それを答えとしてマークします.

4

5 に答える 5

3

protobuf-net は一般に、ほとんどのデータで大幅なスペース節約 (大きさのオーダーなど) を備えており、WCF にアタッチできます。残念ながら、現時点では完全なグラフはサポートされておらず、ツリーのみがサポートされています。ただし、実装する時間がなかっただけの計画があります。約束はできませんが、もう少し早く作業を進めることができます。

さもないと; グラフではなくツリーで動作するように既存のコードを微調整する方法があるかもしれません。

于 2010-12-04T10:40:44.480 に答える
2

以前は、wcfとの間で大きなテキストをやり取りするように実装していました。私のトリガーはそれをストリームに変換し、GZipStreamを使用して圧縮し、byte []として送信します。幸い、10MBを超えることはありません。

あなたの場合、断片化を行うことをお勧めします。シリアル化されたオブジェクトをbyte[]に変換してから、マージして解凍します

psudo

int transferSize = 5000000; // 5MB
byte[] compressed = ...;
var mem = new System.IO.MemoryStream(compressed);

for(int i = 0; i < compressed .length; i+= transferSize )
{
    byte[] buffer = new byte[transferSize];
    mem.Read(buffer, i, compressed);
    mem.Flush();
    sendFragmentToWCF(buffer);
}

2010年12月8日編集

私の理解に基づくと、クライアントがWCFを介して大きなシリアル化オブジェクトをダウンロードしているという状況です。私はこのソリューションで特にテストしませんでしたが、うまくいくはずだと思います。キーは、シリアル化されたオブジェクトをファイルに保存し、応答を使用してそのファイルを送信することです。

[WebMethod]
public void GetSerializedObject()
{
    string path = @"X:\temp.tmp";

    var serializer = new  System.Runtime.Serialization.NetDataContractSerializer();
    var file = new System.IO.FileStream(path, System.IO.FileMode.CreateNew);

    try
    {
        serializer.Serialize(file, ...);
        this.Context.Response.TransmitFile(path);
        this.Context.Response.Flush();
    }
    finally
    {
        file.Flush();
        file.Close();
        file.Dispose();
        System.IO.File.Delete(path);
    }
}

WCFはファイルストリーミングを自動的に実行する必要があり、ファイル送信を使用するため、シリアル化されたオブジェクトのサイズについて心配する必要はありません。構成の応答制限を忘れないでください。

于 2010-12-07T20:44:04.433 に答える
2

それでもメッセージセキュリティを使用する場合は、MTOMを使用して、メッセージの転送に使用する必要のあるネットワーク帯域幅を最適化することをお勧めします。また、セキュリティが適用されるときに、より小さなメモリバッファを使用するためのチャンクチャネルも使用することをお勧めします。そうしないと、WCFはメッセージ全体をメモリにロードしてセキュリティを適用しようとするため、メモリ不足の例外が発生します。

于 2010-06-30T14:31:34.050 に答える
0

誰も公開していないため、おそらくWebSocketsまたはロング ポーリング技術を使用することで、この問題を解決できる可能性があります。私はこれらのソリューションを簡単に調べただけで、それらに関するソリューションを開発していませんが、記録のためにこれらの概念を提案したかったので、時間が許せば後で回答を拡張します.

根底にあるアイデアは、ChunkingChannel の例がどのように機能するかというアイデアに似たものを実現することですが、通常、ポート 80 の Web ベースの要求/応答モデルを壊す全二重チャネルを必要としません。クライアントの関連構成。

その他の関連資料:

更新: これについてさらに調査した結果、NetHttpBinding として知られる WebSockets を使用することで、WCF で wsHttp をメッセージ セキュリティと共に使用するという元の要求を本質的に解決できないことがわかりました。ただし、代替案を検討している可能性のある他の人のための情報として、ここでの回答を保持します。

于 2011-12-12T18:04:49.143 に答える
0

いくつかのより軽いが、保証されていない解決策は、

  • DataContractSerializer両面を所有しているため、代わりに を使用してください。これには、非常に大きな埋め込み型情報は必要ありません。
  • 私が尋ねた質問[DataMember(EmitDefaultValue = false)]で議論されているものを使用してください - 繰り返しますが、あなたは両方の側を所有しているためです。そうすることで、メッセージのサイズがいくらか削減されます (もちろん、グラフ内のデフォルトのフィールドの数に応じて、サイズは異なります)。
  • [DataContract(IsReference=true)]特に繰り返し値オブジェクトまたは参照データが多数ある場合は、 を使用します。
  • サーバーである種のスロットリングを使用して、同時結果のメモリ負荷を軽減します

もちろん、これらは読みやすさなどのトレードオフです。

于 2011-12-05T23:25:49.543 に答える