在具有消息安全性的 WCF 中使用 wsHttp 传输大量数据(序列化对象)

发布于 2024-09-05 07:32:26 字数 3334 浏览 4 评论 0原文

我有一个情况,我需要传输大量序列化对象图(通过 NetDataContractSerializer) 使用 WCF 使用 wsHttp。我正在使用消息安全性,并希望继续这样做。使用此设置,我想传输序列化对象图,该图有时可能接近 300MB 左右,但当我尝试这样做时,我开始看到出现 System.InsufficientMemoryException 类型的异常。

经过一番研究后发现,默认情况下,在 WCF 中,服务调用的结果默认包含在单个消息中,该消息包含序列化数据,并且该数据默认在服务器上缓冲,直到整个消息完全写入。因此,内存异常是由于服务器耗尽了允许分配的内存资源(因为缓冲区已满)而引起的。我遇到的两个主要建议是使用流式传输或分块来解决此问题,但是我不清楚这涉及什么以及我当前的设置(wsHttp/NetDataContractSerializer/Message Security)是否可以使用任一解决方案。到目前为止,我了解到使用流消息安全性是行不通的,因为消息加密和解密需要对整个数据集而不是部分消息进行处理。然而,分块听起来似乎是可能的,但我不清楚如何利用我列出的其他约束来完成分块。如果有人可以就可用的解决方案以及如何实施它提供一些指导,我将不胜感激。

我应该补充一点,就我而言,我真的不担心与其他客户端的互操作性,因为我们拥有并控制通信的每一方,并使用共享接口模式将数据传输到任何一方。因此,我愿意接受任何符合使用 wsHttp 和消息安全性来传输使用 NetDataContractSerializer 序列化的对象图的限制的想法,并且我更喜欢一种无需大幅更改现有服务和周围基础设施的解决方案。

相关资源:

我也对可以在此完成的任何类型的压缩感兴趣数据,但看起来一旦我可以转换到 .NET 4.0,我可能最好在传输级别执行此操作,这样如果我正确理解这一点,客户端将自动支持 gzip 标头。

更新(2010-06-29):

关于我如何得出缓冲消息太大导致我的问题这一结论的一些历史记录。最初,我在测试时看到了下面的 CommunicationException

底层连接已关闭:连接意外关闭。

最终在运行此命令并进行更多日志记录后,我发现了底层InsufficientMemoryException 异常导致指定消息出现问题。

无法分配 268435456 字节的托管内存缓冲区。可用内存量可能较低。

这源于以下方法。

System.ServiceModel.Diagnostics.Utility.AllocateByteArray(Int32 size)

所以换句话说,失败来自于分配数组。当将相同的数据序列化到磁盘时,它占用大约 146MB,如果我将其减少一半,那么我就不再收到错误,但是我还没有深入研究破坏缓冲区的特定阈值以及它是否特定于我的系统或不是。

更新(2010-12-06):

我想此时我正在寻找以下内容的一些澄清。我的理解是,默认情况下,具有消息安全性的 WCF wsHttp 需要在将响应发送回客户端之前在服务器上缓冲整个消息(通常是我返回的整个数据集),从而导致我的问题。

可能的解决方案:

  • 限制数据大小 - 通过使用某种形式的压缩、编码或限制使用某种类似分页的方法返回的实际数据,以避免消耗传出缓冲区的最大容量。
  • 流式传输 - 允许通过 WCF 以流式传输方式发送大量数据,但这与 wsHttp 或 MessageSecurity 不兼容,因为这些技术需要缓冲所有数据。
  • 分块通道 - 允许将数据分解为单独的消息,但此时我不确定这对服务契约设计的限制以及我是否仍然可以将 wsHttp 与消息绑定一起使用。

限制我可以返回的数据只能在一定程度上起作用,并且与流选项一样,这些选项需要在 WCF 服务调用之外编写大量较低级别的工作。因此,我想我需要知道的是,分块通道的任何可能实现是否可以通过允许将一组数据分解为服务器上的单独消息,然后在客户端上拼凑在一起来回避大消息问题。这样我就不必更改现有服务契约的接口/形状,并且该过程几乎对每个服务实现的客户端和服务器部分隐藏,同时仍然使用消息安全性和 wsHttp。如果分块通道要求我重新编写服务合约来公开流,那么我看不出这与流解决方案有什么不同。如果有人可以简单地为我回答这些问题,我将奖励他们并将其标记为答案。

I have a case where I need to transfer large amounts of serialized object graphs (via NetDataContractSerializer) using WCF using wsHttp. I'm using message security and would like to continue to do so. Using this setup I would like to transfer serialized object graph which can sometimes approach around 300MB or so but when I try to do so I've started seeing a exception of type System.InsufficientMemoryException appear.

After a little research it appears that by default in WCF that a result to a service call is contained within a single message by default which contains the serialized data and this data is buffered by default on the server until the whole message is completely written. Thus the memory exception is being caused by the fact that the server is running out of memory resources that it is allowed to allocate because that buffer is full. The two main recommendations that I've come across are to use streaming or chunking to solve this problem however it is not clear to me what that involves and whether either solution is possible with my current setup (wsHttp/NetDataContractSerializer/Message Security). So far I understand that to use streaming message security would not work because message encryption and decryption need to work on the whole set of data and not a partial message. Chunking however sounds like it might be possible however it is not clear to me how it would be done with the other constraints that I've listed. If anybody could offer some guidance on what solutions are available and how to go about implementing it I would greatly appreciate it.

I should add that in my case I'm really not worried about interoperability with other clients as we own and control each side of the communication and use the shared interface pattern for data transfered to either side. So I'm open to any idea that fits inside of the constraints of using wsHttp with message security to transfer object graphs serialized using NetDataContractSerializer and I prefer a solution where I don't have to change my existing services and surrounding infrastructure drastically.

Related resources:

I'm also interested in any type of compression that could be done on this data but it looks like I would probably be best off doing this at the transport level once I can transition into .NET 4.0 so that the client will automatically support the gzip headers if I understand this properly.

Update (2010-06-29):

Some history on how I derived at the conclusion that the buffered message being too large was causing my problem. Originally I saw the CommunicationException below while testing.

The underlying connection was closed: The connection was closed unexpectedly.

Eventually after running this and doing some more logging I found the underlying InsufficientMemoryException exception that was causing the problem with the specified message.

Failed to allocate a managed memory buffer of 268435456 bytes. The amount of available memory may be low.

Which originated from the following method.

System.ServiceModel.Diagnostics.Utility.AllocateByteArray(Int32 size)

So in otherwords the failure came from allocating the array. When writing the same data serialized to disk it takes up around 146MB and if I cut it by half then I stop getting the error however I haven't dug much more into the specific threshold that breaks my buffer and whether it specific to my system or not.

Update (2010-12-06):

I guess at this point I'm looking for some clarifcation for the following. My understanding is that by default with WCF wsHttp with message security that a whole message (generally the whole set of data I'm returning) needs to be buffered on the server before the response is sent back to the client and thus causing my problems.

Possible solutions:

  • Constraining data size - By using some form of compression, encoding, or limiting of actual data returned by using some sort of paging like method in order to avoid consuming the maximum capacity of the outgoing buffer.
  • Streaming - Allows large amounts of data to be sent through WCF in a streaming fashion however this is not compatible with wsHttp or MessageSecurity since these techniques require buffering all the data.
  • Chunking Channel - Allows data to be broken up into separate messages but at this point I'm not sure of the constraints this has on service contract design and whether I can still use wsHttp with message binding.

Limiting the data that I can return only works up to a point and as with the Streaming option these options require coding a lot of the lower level work outside of the WCF service calls. So I guess what I need to know is whether any possible implementation of the chunking channel can side-step the large message issues by allowing a single set of data to be broken up into separate messages on the server and then pieced together on the client in such a way that I do not have to change the interface/shape of existing service contracts and in a way that the process is pretty much hidden from the client and server portion of each service implementation while still using message security and wsHttp. If the chunking-channel is going to require me to re-write my service contracts to expose streams then I don't see how this is really any different than the Streaming solution. If somebody can simply answer these questions for me I'll award them with the bounty and mark it as the answer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

心在旅行 2024-09-12 07:32:27

我曾经实现过将大文本传递给 WCF 或从 WCF 传递大文本。我的三角函数是将其转换为流并使用 GZipStream 对其进行压缩,然后将其作为 byte[] 发送,幸运的是它永远不会超过 10 MB。

对于你的情况,我建议进行碎片化。将序列化对象转换为 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);
}

edit 08 Dec 2010

ok 根据我的理解,情况是客户端通过 WCF 下载一些大型序列化对象。
我没有特别测试这个解决方案,但我猜它应该有效。关键是将序列化对象保存到文件并使用 Response 传输该文件。

[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 应该自动进行文件流,并且您不必担心序列化对象的大小,因为我们使用文件传输。不要忘记配置响应限制。

i used to implement kinda passing big text to/from wcf. my trig is convert it to stream and use GZipStream to compress it then send it as byte[], luckily its never exceed 10 MB.

In your case i recommend do fragmentation. Convert Serialized object to byte[] and then merg it and decompress

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);
}

edit 08 Dec 2010

ok based on my understanding, the situation is client is download some large serialize object throught WCF.
i didn't particularly test on this solution, but guess it should work. The key is save serialized object to file and use Response transmit that file.

[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 shoud do file streaming automatically and u dont ahve to worry about serialized object size since we use file transmit. Dont forget to the config response limit.

负佳期 2024-09-12 07:32:27

一些更轻但不能保证的解决方案是

  • 使用 DataContractSerializer,因为您拥有双方。这不需要嵌入非常大的类型信息。
  • 再次使用在我提出的问题中讨论的[DataMember(EmitDefaultValue = false)] - 再次因为你拥有双方;这样做会减少一些消息大小(当然减少多少取决于图中默认有多少字段)。
  • 使用[DataContract(IsReference=true)],特别是如果您有许多重复的值对象或引用数据,
  • 请在服务器上使用某种限制来减少同时结果的内存压力,

当然,这些是交易 -关闭,例如可读性。

Some lighter, but not guaranteed solutions, would be to

  • use the DataContractSerializer instead since you own both sides. This does not require embedded type information, which is significantly large.
  • use [DataMember(EmitDefaultValue = false)] which is discussed in a question I asked - again because you own both sides; doing so will cut down some on the message size (how much depends of course on how many fields in the graph are defaults).
  • use [DataContract(IsReference=true)], especially if you have many repeated value objects or reference data
  • use some sort of throttling on the server to reduce memory pressure of simultaneous results

These are, of course trade-offs, for example with readability.

夜深人未静 2024-09-12 07:32:27

由于没有人把它放在那里,也许使用 WebSockets 长轮询技术也许能够解决这个问题。我只是简单地研究了这些解决方案,并没有围绕它们开发解决方案,但我想提出这些概念以供记录,如果时间允许,我将在稍后扩展我的答案。

基本想法是实现类似于 ChunkingChannel 示例的工作方式的想法,但不需要全双工通道,这通常会破坏基于端口 80 Web 的请求/响应模型,从而避免必须制作防火墙和其他组件客户端的相关配置。

其他相关材料:

更新:在对此进行更多研究后,似乎通过使用 WebSockets(称为 NetHttpBinding),我将本质上并不能解决原始请求,即在 WCF 中使用 wsHttp 和消息安全性。不过,我将在这里保留我的答案,作为可能正在寻找替代方案的其他人的信息。

Since nobody has put it out there, perhaps using WebSockets or long polling techniques may be able to solve this issue. I've only looked into these solutions briefly and haven't developed a solution around them but I wanted to propose these concepts for the record and I'll expand upon my answer at a later point if time permits.

The underlying idea would be to achieve something similar to how the idea of how the ChunkingChannel example works but while not requiring a full duplex channel which typically breaks the port 80 web based request/response model that is desirable to avoid having to make firewall and other related configurations for clients.

Other related material:

Update: After researching more on this it appears that by using WebSockets, which is known known as NetHttpBinding, that I would inherently not be solving the original request which is to use wsHttp in WCF with message security. I'm going to keep my answer here however as information for others who may be looking into an alternative.

是伱的 2024-09-12 07:32:26

protobuf-net 通常对大多数数据具有显着空间节省(如:数量级),并且可以附加到 WCF。不幸的是目前它不支持完整的图表,只支持树。然而,我在那里有计划,但我只是没有时间去实施。我不能保证任何事情,但我可以尝试更快地完成这项工作。

否则;可能有一些方法可以调整现有代码以使用树而不是图。

protobuf-net generally has a significant space-saving (as in: order-of-magnitude) on most data, and can attach to WCF. Unfortunately at the moment it doesn't support full graphs, only trees. However, I have plans there that I simply haven't had time to implement. I can't promise anything, but I could try to bump that work a bit sooner.

Otherwise; there may be ways to tweak your existing code to work with a tree instead of a graph.

若水般的淡然安静女子 2024-09-12 07:32:26

如果您仍然想使用消息安全性,我建议您使用 MTOM 来优化传输消息所需的网络带宽,以及在应用安全性时使用较小内存缓冲区的分块通道。否则,WCF 将尝试将整个消息加载到内存中以应用安全性,因此您将收到内存不足异常。

If you still want to use Message Security, I would recommend you to use MTOM to optimize the network bandwidth that needs be used to transfer the messages, and also the chunking channel for using smaller memory buffers when security is applied. Otherwise, WCF will try to load the whole message in memory to apply security, and therefore you will get the Insufficient memory exception.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文