WCF中的异步流传输

9
我正在使用WCF进行流处理,并对MSDN中关于WCF中大数据和流处理的文章中“启用异步流处理”段落有疑问。
为了启用异步流处理,需要向服务主机添加DispatcherSynchronizationBehavior终结点行为,并将其AsynchronousSendEnabled属性设置为true。我们还在发送端添加了真正的异步流式处理功能。这提高了服务的可伸缩性,在向多个客户端流式传输消息的情况下特别有效,其中一些客户端由于网络拥塞或根本没有读取而读取速度较慢。在这些场景中,我们现在不会阻止服务每个客户端上的单个线程。这确保了服务能够处理更多的客户端,从而提高了服务的可伸缩性。
我理解上述内容的意思是要添加DispatcherSynchronizationBehavior终结点行为,并将其AsynchronousSendEnabled属性设置为true

<behaviors>
  <endpointBehaviors>
    <behavior name="AsyncStreaming">
      <dispatcherSynchronization asynchronousSendEnabled="true" />
    </behavior>
  </endpointBehaviors>
  ...

我需要修改web.config 文件并在我的端点中引用 AsyncStreaming 行为,但我不明白这些步骤对我有什么作用。我是否需要修改我的代码才能利用这种异步性?

另外,关于类似的话题(如果太不同,我会将其移动到一个新问题),使用异步/等待如何影响在WCF中使用流?我可以在我的服务契约中使用 Task<Stream> Foo() 吗?我进行一些数据库调用,最终将结果包装到自定义流中,然后从WCF服务返回。能够使用像 ExecuteDataReaderAsync() 这样的东西非常有用,当处理流而不是缓冲消息时,我还能使用它吗?

我已经测试过并知道使用Tasks可以“工作”,但我不知道这样做是否会导致函数回退到“缓冲”模式,就像当您向函数提供多个参数时一样(请参阅同一MSDN页面上“流传输的编程模型”的第三段),我也不知道如何检查是否正在发生这种情况。

嗨,Scott!我对任务封装结果有完全相同的疑问。你能否确认它们的使用是否会导致通道恢复为“缓冲”模式? - BlueStrat
2个回答

2

关于一个复杂话题的好问题。我已经实现了一个流式WCF服务来适应大容量(高达2GB)的下载,但是我也有点困惑,关于这个AsyncStreaming=True的问题,因为WCF已经是异步的(每个连接的客户端都会获取自己的线程并异步请求和接收),只要

 <ServiceBehavior(ConcurrencyMode:=ConcurrencyMode.Multiple, InstanceContextMode:=InstanceContextMode.PerCall)>

但您必须更改代码才能使流式传输正常工作。即使您设置了Binding.TransferMode = TransferMode.Streamed,如果您不更改代码以便您的上传和下载功能A)获取并返回数据流,并且B)您的上传和下载函数实现类似于以下内容:

//oBuffer is your content
if (oBuffer != null) {
    oStream = new MemoryStream(oBuffer);

    if (oStream.CanSeek) {
        oStream.Seek(0, SeekOrigin.Begin);
    }
    return oStream;
}

这是一篇不错的 HowTo 文章,我用它作为指南:http://www.codeproject.com/Articles/166763/WCF-Streaming-Upload-Download-Files-Over-HTTP

对于您的第二部分,我的意思是从“asyncStreaming=false”转换到“asyncStreaming=true”,我是否需要对我的代码进行任何更改,我并不是指更改我的代码以支持“TransferMode.Streaming”,那个更改已经很久以前完成了。 - Scott Chamberlain
我没有使用asyncStreaming=True或=False,但程序仍然可以异步工作。是的,您需要将Transfermode更改为Streaming,但还需要更改代码,以便返回Stream并且它可以寻址。这样做的作用是允许客户端(必须以类似的方式设置)请求流的下一部分。我花了一段时间才理解WCF流式传输的工作原理,但可以将其视为文件对象。 - Brian
当您编写一个程序来读取大文件时,程序不会等待整个文件被读取后再将字节返回给程序。相反,它会在读取(一些)内容并输出字节的同时进行(即异步)。WCF也是这样工作的,这就是为什么它对于大数据传输非常有用的原因。它开始读取,但在读取所有字节之前,它已经将其拥有的字节发送到客户端。在客户端接收第一组字节后,客户端将联系主机(在幕后,您无需编程),以获取下一组字节。 - Brian
因此,客户端程序有效地接管了进程的控制,请求更多字节直到接收到所有数据。明白吗? - Brian

2
我通过 .NET 参考源代码发现,问题出在 RequestContext。显然,ChannelHandler.sendAsynchronously 字段控制消息回复是异步完成(通过 RequestContext.BeginReply/EndReply APM 方法)还是同步完成(通过 RequestContext.Reply)。
据我所知,这只是释放了一个服务器端线程并将其返回到线程池中,否则该线程将在服务器上一直忙于“抽取”流并传输给客户端,只要服务器上的 Stream 对象存在。
这似乎是完全透明的,因此我认为您可以安全地使用基于 TAP 的异步契约方法并返回 Task<Stream>。例如,在另一个契约方法中,您可以执行 await Stream.WriteAsync
当您到达那里时,请分享您的实际经验作为您自己的答案,我会非常感兴趣了解细节 :)

我是否必须使用“await”语句才能释放线程? - Sal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接