Fra*_*ori 21 .net c# silverlight wcf
我有一个自托管的WCF服务(v4框架),它通过HttpTransport基于自定义的绑定公开.绑定使用的自定义MessageEncoder几乎BinaryMessageEncoder与添加gzip压缩功能有关.
Silverlight和Windows客户端使用Web服务.
问题:在某些情况下,服务必须返回非常大的对象,并且在响应多个并发请求时偶尔会抛出OutOfMemory异常(即使任务管理器报告该过程约为600 Mb).当消息即将被压缩时,自定义编码器中发生异常,但我认为这只是一种症状,而不是原因.例外情况是"未能分配x Mb",其中x为16,32或64,而不是一个过大的数量 - 因此我相信其他事情已经使这个过程接近某个限制.
服务端点定义如下:
var transport = new HttpTransportBindingElement(); // quotas omitted for simplicity
var binaryEncoder = new BinaryMessageEncodingBindingElement(); // Readerquotas omitted for simplicity
var customBinding = new CustomBinding(new GZipMessageEncodingBindingElement(binaryEncoder), transport);
然后我做了一个实验:我改变TransferMode从Buffered到StreamedResponse  (和相应的修改的客户端).这是新的服务定义:
var transport = new HttpTransportBindingElement()
{
    TransferMode = TransferMode.StreamedResponse // <-- this is the only change
};
var binaryEncoder = new BinaryMessageEncodingBindingElement(); // Readerquotas omitted for simplicity
var customBinding = new CustomBinding(new GZipMessageEncodingBindingElement(binaryEncoder), transport);
神奇的是,没有OutOfMemory异常了.对于小消息,服务有点慢,但随着消息大小的增加,差异变得越来越小.行为(速度和OutOfMemory异常)都是可重现的,我用两种配置做了几次测试,这些结果是一致的.
问题解决了,但是:我无法解释自己这里发生了什么.令我惊讶的是,我没有以任何方式改变合同.即我没有像单个Stream参数等那样创建合同,就像通常对流式消息一样.我仍在使用具有相同DataContract和DataMember属性的复杂类.我只修改了端点,就是这样.
我认为设置TransferMode只是一种为正确形成的合同启用流式传输的方法,但显然还有更多.当你改变时,有人可以解释一下实际发生的事情TransferMode吗?
Eug*_*sky 19
当您使用'GZipMessageEncodingBindingElement'时,我假设您正在使用MS GZIP示例.
看看DecompressBuffer()GZipMessageEncoderFactory.cs,您将了解缓冲模式下发生了什么.
例如,假设您有一条未压缩大小为50M,压缩大小为25M的消息.
DecompressBuffer将收到(1)25M大小的'ArraySegment buffer'参数.然后,该方法将创建一个MemoryStream,使用(2)50M将缓冲区解压缩到其中.然后它将执行一个MemoryStream.ToArray(),将内存流缓冲区复制到一个新的(3)50M大字节数组中.然后它需要来自AT LEAST(4)50M +的BufferManager的另一个字节数组,实际上它可以更多 - 在我的情况下,50M阵列总是67M.
在DecompressBuffer结束时,(1)将返回到BufferManager(它似乎永远不会被WCF清除),(2)和(3)受GC(这是异步,如果你比GC更快) ,你可能会得到OOM异常,即使有足够的内存,如果清理的话).(4)可能会被返回到BinaryMessageEncodingBindingElement.ReadMessage()中的BufferManager.
总而言之,对于您的50M消息,您的缓冲方案将暂时占用25 + 50 + 50 +例如65 = 190M内存,其中一些受异步GC的影响,其中一些由BufferManager管理,最坏的情况是 - 意味着它在内存中保留了大量未使用的数组,这些数组既不能用于后续请求(例如太小),也不适用于GC.现在想象你有多个并发请求,在这种情况下,BufferManager将为所有并发请求创建单独的缓冲区,永远不会被清除,除非你手动调用BufferManager.Clear(),我不知道如何做到这一点使用WCF使用的缓冲区管理器,另请参阅此问题:如何防止WCF客户端应用程序中的BufferManager/PooledBufferManager浪费内存?]
更新:迁移到IIS7 Http压缩(wcf条件压缩)内存消耗,cpu加载和启动时间丢失(没有数字方便)然后从缓冲迁移到流式TransferMode(如何防止我的WCF中的BufferManager/PooledBufferManager客户端应用程序浪费内存?)我的WCF客户端应用程序的内存消耗从630M(峰值)/ 470M(连续)下降到270M(峰值和连续)!
Bry*_*nny 12
我有一些WCF和流媒体的经验.
基本上,如果你没有设置TransferMode流式传输,那么它将默认为缓冲.因此,如果您要发送大量数据,它将在内存中构建数据,然后在加载所有数据并准备发送后发送.这就是为什么你出现内存不足的原因,因为数据非常庞大而且超出了你机器的内存.
现在,如果您使用流式传输,那么它将立即开始将数据块发送到另一个端点而不是缓冲它,从而使内存使用非常少.
但这并不意味着接收器也必须设置为流式传输.它们可以设置为缓冲区,并且如果它们没有足够的内存用于数据,则会遇到与发送方相同的问题.
为获得最佳结果,应设置两个端点以处理流(对于大型数据文件).
通常,对于流式传输,您使用MessageContracts而不是DataContracts因为它使您可以更好地控制SOAP结构.
有关详细信息,请参阅有关MessageContracts和Datacontracts的这些MSDN文章.这里有更多关于Buffered vs Streamed的信息.
| 归档时间: | 
 | 
| 查看次数: | 33906 次 | 
| 最近记录: |