我有一个案例,我需要使用 WCF 使用 wsHttp传输大量序列化对象图(通过NetDataContractSerializer )。我正在使用消息安全,并希望继续这样做。使用此设置,我想传输序列化的对象图,它有时可能接近 300MB 左右,但是当我尝试这样做时,我开始看到 System.InsufficientMemoryException 类型的异常出现。
经过一番研究,默认情况下,在 WCF 中,服务调用的结果似乎包含在默认情况下包含在单个消息中,该消息包含序列化数据,并且默认情况下此数据在服务器上缓冲,直到整个消息被完全写入。因此,由于该缓冲区已满,服务器正在耗尽允许分配的内存资源,从而导致内存异常。我遇到的两个主要建议是使用流式处理或分块来解决这个问题,但是我不清楚这涉及到什么,以及在我当前的设置(wsHttp/NetDataContractSerializer/Message Security)下这两种解决方案是否可行。到目前为止,我知道使用流式消息安全性是行不通的,因为消息加密和解密需要对整个数据集而不是部分消息起作用。然而,分块听起来可能是可能的,但我不清楚如何使用我列出的其他约束来完成。如果有人可以就可用的解决方案以及如何实施它提供一些指导,我将不胜感激。
我应该补充一点,就我而言,我真的不担心与其他客户端的互操作性,因为我们拥有并控制通信的每一端,并使用共享接口模式将数据传输到任一端。因此,我对任何符合使用带有消息安全性的 wsHttp 来传输使用 NetDataContractSerializer 序列化的对象图的限制的想法持开放态度,我更喜欢不需要大幅更改现有服务和周围基础设施的解决方案。
相关资源:
- 分块频道
- 如何:启用流式传输
- WCF 上的大型附件
- 自定义消息编码器
- InsufficientMemoryException 的另一个发现
- 需要非双工分块通道
- 使用 WCF 和延迟执行流式传输大型内容
我也对可以对这些数据进行的任何类型的压缩感兴趣,但是一旦我可以转换到 .NET 4.0 以便客户端将自动支持 gzip,我可能最好在传输级别执行此操作标题,如果我理解正确的话。
更新(2010-06-29):
关于我如何得出缓冲消息太大导致我的问题的结论的一些历史。最初我在测试时看到了下面的CommunicationException。
基础连接已关闭:连接意外关闭。
最终,在运行此程序并进行更多日志记录后,我发现导致指定消息出现问题的底层InsufficientMemoryException异常。
未能分配 268435456 字节的托管内存缓冲区。可用内存量可能很低。
这源于以下方法。
System.ServiceModel.Diagnostics.Utility.AllocateByteArray(Int32 大小)
所以换句话说,失败来自分配数组。将序列化的相同数据写入磁盘时,它占用大约 146MB,如果我将其减半,那么我将停止收到错误,但是我没有深入研究破坏缓冲区的特定阈值以及它是否特定于我的系统或不是。
更新(2010-12-06):
我想此时我正在为以下内容寻找一些澄清。我的理解是,默认情况下,具有消息安全性的 WCF wsHttp 在将响应发送回客户端之前需要在服务器上缓冲整条消息(通常是我返回的整组数据),从而导致我的问题。
可能的解决方案:
- 限制数据大小 - 通过使用某种形式的压缩、编码或限制使用某种类似分页的方法返回的实际数据,以避免消耗传出缓冲区的最大容量。
- 流式传输 - 允许通过 WCF 以流式传输方式发送大量数据,但这与 wsHttp 或 MessageSecurity 不兼容,因为这些技术需要缓冲所有数据。
- 分块通道 - 允许将数据分解为单独的消息,但此时我不确定这对服务合同设计的限制,以及我是否仍然可以将 wsHttp 与消息绑定一起使用。
限制我可以返回的数据只能在一定程度上起作用,并且与 Streaming 选项一样,这些选项需要对 WCF 服务调用之外的许多较低级别的工作进行编码。所以我想我需要知道的是,分块通道的任何可能实现是否可以通过允许将一组数据分解为服务器上的单独消息,然后在客户端上拼凑在一起,从而避开大消息问题这样我就不必更改现有服务合同的接口/形状,并且该过程几乎对每个服务实现的客户端和服务器部分隐藏,同时仍使用消息安全性和 wsHttp。如果分块通道将要求我重写我的服务合同以公开流,那么我不会' 看不出这与 Streaming 解决方案有何不同。如果有人可以简单地为我回答这些问题,我将奖励他们并将其标记为答案。