委派同步 WCF 调用以启动异步进程

Posted

技术标签:

【中文标题】委派同步 WCF 调用以启动异步进程【英文标题】:Delegating a synchronous WCF call to start an asynchronous process 【发布时间】:2017-02-01 18:51:09 【问题描述】:

我需要修改一个 WCF 方法以异步运行。 [OperationContract(IsOneWay=true)] 在我的情况下不是一个有效的选项,因为客户端期望返回值。我无法控制客户端对现有 WCF 方法调用的使用,我无法更改签名以返回 void. 我也无法修改客户端以强制执行异步 WCF 调用。客户端仍然需要调用其同步 WCF 调用并获得其硬编码的响应。

在服务中,存在管理监控和异常的行为。我正在考虑的选项是让 WCF 服务自行注册或自行发现其端点,向服务添加 One-Way WCF 方法,并让同步方法通过 WCF 调用 One-Way 方法。我认为这将是确保所有现有行为仍然适用的最安全方法。这些行为不收集或依赖于来自最终客户端的任何信息。虽然从现有同步调用中启动其他线程或异步调用在技术上似乎是可行的,但我不喜欢这样,因为我认为它可能会破坏在这一点上与当前 WCF OperationContext 相关联的状态信息。

似乎我探索过的所有与此相关的选项都需要某种妥协,并且将同步调用转发到单向调用的选项将是最安全的。有没有更好的方法来解决这个问题,或者是否可以强制 WCF 方法向客户端提供与 OneWay 调用相同的立即返回,但包含返回值?

【问题讨论】:

如果签名返回 void,是否不能将方法体扔到 Task.Run() 中,或者使用 TPL 中的 DataFlow? 我担心的是异常和状态监控行为中使用了工作流信息。有一个有状态的对象存储在 OperationContext 中,用于维护有关进程的信息。例如,如果出现异常,它将所有相关的流程信息保存为一个包。我不知道 OperationContext 是否会坚持使用通过 TPL 启动的内容。 【参考方案1】:

有几种方法可以解决这个问题。您已经确定了其中之一(“链式 WCF 调用”)。另一个确实涉及 TPL 技术和技巧。

链式 WCF 调用。

使用这种方法,请记住 WCF 交互中最昂贵的部分是负载/参数的(反)序列化。就延迟而言,您的客户将为此付出两倍的代价,因为您已将端点“链接”在一起。 “oneway”调用配置仍然需要客户端“等待”,直到所有参数都正确反序列化并且调用“看起来不错”。这是因为单向调用仍然能够将 SOAP 异常返回给调用者,指示非法参数或崩溃的 WCF 行为等。只有“端点工作”(您编程的部分)的延迟将被“保存”使用这种技术。

使用这种方法,您还必须考虑必须为您的单向例程配置与原始端点的大部分或全部相同的行为。对于异常处理方面尤其如此。你说:

有一个有状态的对象存储在 OperationContext 中,它维护有关进程的信息。例如,如果出现异常,它会将所有相关的流程信息保存为一个包。

听起来您想从原始入口点剥离所有或大部分行为,而是让它们装饰您的内部单向例程。

TPL 技巧

Note: this next approach likely solves only half your problem...
because it does not address the WCF exception-handing scenario 
you called out.

一般来说,因为它是 [ThreadStatic],所以您担心 OperationContext 不在线程和 TPL 例程之间流动是正确的。但是,根据 MSDN,OperationContext.Current 属性是公共的。使用闭包,您应该能够手动将 WCF 请求线程中的 OperationContext 分配到新任务的新 OperationContext.Current。

在这种情况下,您可以允许主 WCF 请求线程返回......但新任务可以使用相同的 OperationContext 引用运行。如果在新任务上抛出异常,它肯定不会由自定义 WCF 行为处理,因为主 WCF 请求线程可能早就退出并返回,因此它已经“通过”所有(异常处理)行为层。

简而言之,这给您带来的唯一好处是,如果 WCF 行为已将事务 ID 标记到 OperationContext 中,那么该事务 ID 将手动流向新任务...允许下游例程获取它.

如果您要遵循这种方法,那么:

尝试确保仍有回调等待后台任务...其目的是捕获任何异常,必要时记录它,然后吞下。您不希望 WCF 应用程序中的独立任务具有未处理的异常。请记住,在这种情况下,您将无法从 WCF 行为中获得帮助。 确保任务具有相当有限的延迟...例如 400 到 800 毫秒。您不希望任务运行 5 分钟。大概您的 WCF 应用程序是 IIS 托管的,并且 IIS 可以在没有警告的情况下重新加载或卸载您的 WCF AppDomain...因为它不知道您已经创建了一个仍在工作的新任务。只要延迟受到限制,我怀疑您会遇到 IIS“过早”关闭 WCF 应用的问题。

最后,如果您的情况比这更复杂,请注意custom TaskScheduler instances。

【讨论】:

以上是关于委派同步 WCF 调用以启动异步进程的主要内容,如果未能解决你的问题,请参考以下文章

C#异步编程

WCF 中长时间运行进程的进度通知 - 如何?

同步和异步阻塞和非阻塞线程进程和协程异同的理解

NIO:同步异步和阻塞非阻塞 buffer缓冲区

并发,并行,同步,异步的区别

并发,并行,进程,线程,同步,异步