gpt4 book ai didi

c# - REST WCF - 流下载非常慢,65535 (64KB) block 无法更改

转载 作者:行者123 更新时间:2023-11-30 17:46:23 26 4
gpt4 key购买 nike

我们有一个返回流的 WCF 方法 - 通过 REST 公开。我们将常规下载(从网站)与 WCF 方法进行了比较,发现 70MB 文件的内容如下:

  • 在常规站点中 - 下载大约需要 10 秒 - 1MB block 大小
  • 在 WCF 方法中 - 大约需要 20 秒 - block 大小总是65,535 字节

我们有一个自定义流,它实际上流到另一个产品中,这使得时间差异变得更糟 - 常规站点需要 1 分钟,而 WCF 需要 2 分钟。

因为我们需要支持非常大的文件 - 它变得至关重要。

我们在调试时停止,并发现 WCF 调用的 Stream 方法“Read”始终具有 65,535 的 block 大小 - 导致缓慢

我们尝试了几种服务器配置 - 像这样:

端点:

   <endpoint address="Download" binding="webHttpBinding" bindingConfiguration="webDownloadHttpBindingConfig"  behaviorConfiguration="web" contract="IAPI" />

绑定(bind):

<binding name="webDownloadHttpBindingConfig" maxReceivedMessageSize="20000000" maxBufferSize="20000000" transferMode="Streamed">
<readerQuotas maxDepth="32" maxStringContentLength="20000000" maxArrayLength="20000000" maxBytesPerRead="20000000" maxNameTableCharCount="20000000"/>
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
</security>
</binding>

作为 REST 客户端的客户端(不能使用 WCF 绑定(bind)——我们不想引用它)——是这样构建的:

System.Net.HttpWebRequest request = (HttpWebRequest)WebRequest.Create(CombineURI(BaseURL, i_RelativeURL));

request.Proxy = null; // We are not using proxy
request.Timeout = i_Timeout;
request.Method = i_MethodType;
request.ContentType = i_ContentType;

string actualResult = string.Empty;
TResult result = default(TResult);
if (!string.IsNullOrEmpty(m_AuthenticationToken))
{
request.Headers.Add(ControllerConsts.AUTH_HEADER_KEY, m_AuthenticationToken);
}

using (var response = request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
byte[] buffer = new byte[1048576];

int read;
while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
o_Stream.Write(buffer, 0, read);
}
}
}

基本上我们只是将流式传输到流中。

所以,无论我们做什么——服务器总是接收到大小为 65,535 的 block (我们尝试了几种客户端/服务器配置)

我们缺少什么?

谢谢!

== 编辑 2015 年 8 月 4 日微软回应 ==您好,我们与微软合作处理了这个案例,这是他们的回答:

当 WCF 客户端调用返回 Stream 的 WCF 方法时,它实际上获得了对 MessageBodyStream 实例的引用。MessageBodyStream 最终依赖于 WebResponseInputStream 来实际读取数据,通过这个关系图:

  • MessageBodyStream 有一个成员 message,它引用了一个 InternalByteStreamMessage 实例
  • InternalByteStreamMessage 有一个引用 StreamBasedStreamedBodyWriter 实例的成员 bodyWriter
  • StreamBasedStreamedBodyWriter 有一个引用 MaxMessageSizeStream 实例的成员流
  • MaxMessageSizeStream 有一个引用 WebResponseInputStream 实例的成员流

当您在流上调用 Read() 时,WebResponseInputStream.Read() 最终会被调用(您可以通过在 Visual Studio 中设置断点来自行测试 - 一个警告:Visual Studio 中的“Just My Code”选项 - 调试必须被禁用,以便命中断点)。WebResponseInputStream.Read() 的相关部分如下:

                    return BaseStream.Read(buffer, offset, Math.Min(count, maxSocketRead));

其中 maxSocketRead 被定义为 64KB。maxSocketRead 上面的评论说“为了避免破坏内核缓冲区,我们限制了我们的读取。 http.sys 可以很好地处理这种情况,但 System.Net 不会进行任何此类限制。”。这意味着,如果您指定的读取值太大,则会超出内核自身的缓冲区大小,并导致性能下降,因为它需要做更多的工作。

这会导致性能瓶颈吗?不,不应该。一次读取太少的字节(比如 256 字节)会导致性能下降。但是 64KB 应该是一个可以带来良好性能的值。在这些情况下,真正的瓶颈通常是网络带宽,而不是客户端读取数据的速度。为了最大化性能,重要的是读取循环尽可能紧密(换句话说,读取之间没有明显的延迟)。我们还要记住,大于 80KB 的对象会进入 .Net 中的大对象堆,它的内存管理效率低于“正常”堆(正常情况下不会发生压缩,因此可能会出现内存碎片)。

最佳答案

我们与微软就此案例进行了合作,这是他们的答复:

当 WCF 客户端调用返回 Stream 的 WCF 方法时,它实际上获得了对 MessageBodyStream 实例的引用。 MessageBodyStream 最终依赖于 WebResponseInputStream 来实际读取数据,通过这个关系图:

MessageBodyStream 有一个成员 message,它引用了一个 InternalByteStreamMessage 实例InternalByteStreamMessage 有一个成员 bodyWriter,它引用了一个 StreamBasedStreamedBodyWriter 实例StreamBasedStreamedBodyWriter 有一个成员流,它引用了一个 MaxMessageSizeStream 实例MaxMessageSizeStream 有一个成员流,它引用了一个 WebResponseInputStream 实例当您在流上调用 Read() 时,WebResponseInputStream.Read() 最终会被调用(您可以通过在 Visual Studio 中设置断点来自行测试 - 一个警告:Visual Studio 中的“仅我的代码”选项 - 必须禁用调试,为了命中断点)。 WebResponseInputStream.Read() 的相关部分如下:

                return BaseStream.Read(buffer, offset, Math.Min(count, maxSocketRead));

其中 maxSocketRead 被定义为 64KB。 maxSocketRead 上面的评论说“为了避免破坏内核缓冲区,我们限制了我们的读取。 http.sys 可以很好地处理这种情况,但 System.Net 不会进行任何此类限制。”。这意味着,如果您指定的读取值太大,则会超出内核自身的缓冲区大小,并导致性能下降,因为它需要做更多的工作。

这会导致性能瓶颈吗?不,不应该。一次读取太少的字节(比如 256 字节)会导致性能下降。但是 64KB 应该是一个可以带来良好性能的值。在这些情况下,真正的瓶颈通常是网络带宽,而不是客户端读取数据的速度。为了最大化性能,重要的是读取循环尽可能紧密(换句话说,读取之间没有明显的延迟)。我们还要记住,大于 80KB 的对象会进入 .Net 中的大对象堆,它的内存管理效率低于“正常”堆(正常情况下不会发生压缩,因此可能会出现内存碎片)。

可能的解决方案:是在内存中缓存更大的 block (例如,使用 MemoryStream 并且当 WCF 流调用您的自定义“读取”时 - 缓存在 1MB 或更多/更少 - 任何你想要的。

然后,当超过 1MB(或其他值)时 - 将其推送到您的实际自定义流,并继续缓存更大的 block

这没有经过检查,但我认为它应该可以解决性能问题。

关于c# - REST WCF - 流下载非常慢,65535 (64KB) block 无法更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26323702/

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