首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在具有消息安全性的WCF中使用wsHttp传输大量数据(序列化对象)

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

Stack Overflow用户
提问于 2010-06-11 00:39:01
回答 5查看 14.5K关注 0票数 16

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

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

我应该补充说,在我的情况下,我真的不担心与其他客户端的互操作性,因为我们拥有并控制通信的每一端,并使用共享接口模式将数据传输到任何一端。因此,我对任何符合使用具有消息安全性的wsHttp来传输使用NetDataContractSerializer序列化的对象图的约束的想法都持开放态度,并且我更喜欢这样一种解决方案,即我不必彻底更改现有服务和周围的基础设施。

相关资源:

我也对可以对这些数据执行的任何类型的压缩感兴趣,但是看起来一旦我可以过渡到gzip 4.0,那么在传输级别做这件事可能是最好的,这样如果我理解正确的话,客户端将自动支持.NET头文件。

更新(2010-06-29):

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

基础连接已关闭:连接意外关闭。

最后,在运行此程序并执行更多日志记录后,我找到了导致指定消息出现问题的底层InsufficientMemoryException异常。

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

它源于下面的方法。

System.ServiceModel.Diagnostics.Utility.AllocateByteArray(Int32大小)

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

更新(2010-12-06):

我想在这一点上,我想要澄清以下几点。我的理解是,默认情况下,带有消息安全性的WCF wsHttp需要在服务器上缓冲整个消息(通常是我返回的整个数据集),然后才能将响应发送回客户端,从而导致我的问题。

可能的解决方案:

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

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

EN

回答 5

Stack Overflow用户

发布于 2010-12-04 18:40:44

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

否则,可能有办法调整现有代码,使其使用树而不是图形。

票数 3
EN

Stack Overflow用户

发布于 2010-06-30 22:31:34

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

票数 2
EN

Stack Overflow用户

发布于 2010-12-08 04:44:04

我曾经实现过向/从wcf传递大文本。我的触发器是将其转换为流,并使用GZipStream压缩,然后将其作为byte[]发送,幸运的是它从来没有超过10MB。

在您的情况下,我建议使用碎片。将序列化对象转换为byte[],然后合并并解压缩

psudo

代码语言:javascript
复制
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下载一些大的序列化对象。我没有特别测试这个解决方案,但我猜它应该可以工作。关键字是将序列化对象保存到文件,并使用响应传输该文件。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3016446

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档