gpt4 book ai didi

c# - WCF HttpTransport : streamed vs buffered TransferMode

转载 作者:IT王子 更新时间:2023-10-29 04:38:25 27 4
gpt4 key购买 nike

我有一个自承载的 WCF 服务(v4 框架),它通过基于 HttpTransport 的自定义绑定(bind)公开。绑定(bind)使用自定义 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);

然后我做了一个实验:我将 TransferModeBuffered 更改为 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 异常)是可重现的,我对这两种配置进行了多次测试,这些结果是一致的。

问题已解决,但是:我无法解释这里发生了什么。我的惊讶源于我没有以任何方式更改契约(Contract)。 IE。我没有像您通常为流式消息所做的那样使用单个 Stream 参数等创建契约(Contract)。我仍在使用具有相同 DataContract 和 DataMember 属性的复杂类。 我只是修改了端点,仅此而已。

我认为设置 TransferMode 只是一种为正确形成的契约(Contract)启用流式传输的方法,但显然不止于此。任何人都可以解释一下当您更改 TransferMode 时实际上发生了什么吗?

最佳答案

当您使用“GZipMessageEncodingBindingElement”时,我假设您使用的是 MS GZIP 示例。

查看 GZipMessageEncoderFactory.cs 中的 DecompressBuffer(),您将了解缓冲模式下发生的情况。

举例来说,假设您有一条消息,未压缩大小为 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 + e.g. 65 = 190M 内存,其中一些受异步 GC 影响,一些由 BufferManager 管理,这 - 最坏的情况 - 意味着它在内存中保留了大量未使用的数组,这些数组在后续请求中不可用(例如,太小)也不符合 GC 的条件。现在假设您有多个并发请求,在这种情况下,BufferManager 将为所有并发请求创建单独的缓冲区,这些缓冲区永远不会被清理,除非您手动调用 BufferManager.Clear(),而我不会了解使用 WCF 使用的缓冲区管理器执行此操作的方法,另请参阅以下问题:How can I prevent BufferManager / PooledBufferManager in my WCF client app from wasting memory? ]

更新:迁移到 IIS7 Http 压缩后 ( wcf conditional compression ) 内存消耗、cpu 负载和启动时间下降(手头没有数字)然后从缓冲迁移到流式 TransferMode ( How can I prevent BufferManager / PooledBufferManager in my WCF client app from wasting memory? )我的 WCF 客户端应用程序的内存消耗已从 630M(峰值)/470M(连续)下降到 270M(峰值和连续)!

关于c# - WCF HttpTransport : streamed vs buffered TransferMode,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4043683/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com