【问题标题】:Call synchronous webservice methods asynchronously异步调用同步 Web 服务方法
【发布时间】:2016-09-19 18:09:21
【问题描述】:

我必须在每次循环中使用不同的参数调用一个 web 服务方法大约一百次。 网络服务只有同步方法。目前我正在控制台应用程序中对此进行测试,同步执行时需要十多分钟才能获取数据!

我想要什么: 并行运行 10 个请求。当他们完成后,执行接下来的十次调用。 这当然应该是异步的。

该功能将托管在 IIS 托管的 wcf 服务中。

概述: 客户端使用参数调用 wcf 服务一次。 wcf 服务方法应该异步调用另一个 web 服务一百次,并将最终数据保存到 Excel。

我读到,Task.Run 在 Web 应用程序中使用时不是一个好主意。

那么,如何在 Web 上下文中异步调用同步 Web 服务方法?

我正在使用CRM Microsoft.Xrm.Sdk,Version=7.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35

  var orders = _serviceProxy.RetrieveMultiple( new FetchExpression(strXmlFetch));

RetrieveMultiple 获取定义查询并执行请求的 xml 片段 (strXmlFetch)。 该方法驻留在此处:

命名空间 Microsoft.Xrm.Sdk.Client
OrganizationServiceProxy

public EntityCollection RetrieveMultiple(QueryBase query);

在后台,客户端 SDK 在调用 RetrieveMultiple 时会执行以下操作。

using (OrganizationServiceContextInitializer organizationServiceContextInitializer = new OrganizationServiceContextInitializer(this))
{
    entityCollection = base.ServiceChannel.Channel.RetrieveMultiple(query);
    return entityCollection;
}

我还没有实现任何东西,因为我需要一个异步执行请求的起点。

【问题讨论】:

  • 你可以使用 Parallel.ForEach (msdn.microsoft.com/en-us/library/…)
  • @Evk Parallel API for Network IO,绝对不是一个好主意,会浪费资源,它应该是 Async-Await,内部调用同步方法,或者如果可用则可能是异步
  • 但是 OP 说他的网络服务只有同步方法。当然,最好利用异步 IO,但问题表明这是不可能的。如果您将使用 async\await 和 Task.Run 调用此同步方法 - 将不会改变任何内容。
  • 为什么不能调用它,只是它需要在 Sync 上使用 Async 包装器,这是 Anti Pattern,但这是使用 Async-Await 异步调用同步方法的唯一方法
  • 你能给我们看一些客户端的代码吗?我对 Web 服务如何完全同步感到困惑。客户端正在点击 URL 并等待响应 - 从客户端的角度来看,这绝对可以是异步的

标签: c# web-services asynchronous task


【解决方案1】:

这当然应该是异步的。

嗯,那将是理想的。但是,OrganizationServiceProxy does not have asynchronous methodsneither does IOrganizationService,因此直接使用 ServiceChannel.Channel 也无济于事)。

因此,首先要做的是要求 Microsoft 将异步 API 添加到该客户端库。

我读到,在 Web 应用程序中使用 Task.Run 并不是一个好主意。

通常是这样;您想调用异步 API。在这种情况下不可用。

那么,如何在 Web 上下文中异步调用同步 Web 服务方法?

这是不可能的。您的选择是:

  1. 保持同步,一次一个。是的,它需要的时间比预期的要长。
  2. 使其平行

服务器端并行处理的问题在于,您现在使用N 线程处理单个请求,这非常快会使您的网络服务器瘫痪。我不建议在生产代码中使用这种方法,尤其是当它通过 Internet 公开时。

但是,如果您确定不会有太多对 WCF 服务的调用,您可以并行实现它。

由于您需要从并行查询中收集结果,我建议使用 Parallel LINQ (AsParallel) 指定 WithDegreeOfParallelism(10),类似:

IEnumerable<EntityCollection> GetAll(OrganizationServiceProxy proxy, IEnumerable<QueryBase> queries)
{
  return queries.AsParallel().WithDegreeOfParallelism(10)
      .Select(query => proxy.RetrieveMultiple(query));
}

【讨论】:

  • 目前我使用 Task.Run 来完成,但使用 ParallelForEach 会更聪明、更容易。因此,例如,我需要获取 50000 条记录,在进行同步时,50 个调用每个返回 1000,每个请求需要 5 秒,总共 250 秒。使用并行性 (WithDegree(50)) 我希望它大约在 5 到 8 秒内完成。但就我而言,在同步模式下运行需要 90% 的时间。我猜服务器端有东西阻塞...?
  • 在我的代码中我使用WithDegreeOfParallelism(10),20 我开始收到超时。
  • @Legends:服务器端很有可能一次将您限制为一个请求。或者你遇到了其他瓶颈。在这种情况下可能会发生超时,因为超时时间是从您发出请求到结果到达 - 如果服务器正在限制您,那么您的某些请求将人为地延长时间。
【解决方案2】:

网络服务只有同步方法

Web 服务默认产生与BeginXX, EndXX 相关的Asynchronous 方法,它们是Asynchronous Programming Model 的一部分,但它们不是Async-Await 可以使用的方法,因为它们不返回@987654325 @。

我想要什么:并行运行 10 个请求。当他们完成后,执行接下来的十次调用。这当然应该是异步的。

在真正的 Async 调用中,因为没有调用 threads 并且它在 IO 完成端口上工作,因此您可以启动更多的调用,而不仅仅是 10 个。否则一次安排 10 个异步请求的逻辑必须自定义,没有开箱即用的机制来做到这一点,即使使用Threadpool,也不能保证并行请求的数量,尽管您可以设置Max Degree of Parallelism,以获得更高的并行API限制。

概述:客户端使用参数调用 wcf 服务一次。 wcf 服务方法应该异步调用另一个 web 服务一百次,并将最终数据保存到 Excel。

您希望所有调用都是异步的,这样 Ui 线程就不会被阻塞,无论是对 wcf 服务还是 Web 服务。

我读到Task.Run 在 Web 应用程序中使用时不是一个好主意。

没错,因为 ThreadPool 线程正在被调用,在将 IO 调用分派到 Web Service 后,它不会做任何事情

那么,如何在 Web 上下文中异步调用同步 Web 服务方法?

需要一个异步包装器而不是同步方法,这是反模式,但没有其他选择。这需要一个ThreadPool 线程,类似于:

public class AsyncWrapper
{   
  public async Task CallWcfAsync(<Parameters>)
  {  
       SynchronizationContext _synchronizationContext = 
       SynchronizationContext.Current;

    try
    {
      SynchronizationContext.SetSynchronizationContext(null);
      await Task.Run(() => CallWcfMethod(<Parameters>));
    }
    finally
    {             
      SynchronizationContext.SetSynchronizationContext
      (_synchronizationContext);
    }   
  }
}

要点:

  1. CallWcfAsync 是您需要的Async wrapper method

  2. 1234563 on 被屏蔽了。

【讨论】:

  • 所以最后这个包装器和使用 Parallel.ForEach 一样,只是不太方便,因为需要自己实现批处理(最多 10 个并行请求)。
  • @Evk 再想一想,这将比使用普通的Parallel.For 有更多的好处,除了拥有SynchronizationContext 的控制权,为了避免死锁,它不会像 Parallel 那样阻塞调用,所以会腾出调用/ui线程来处理其他任务,这就是IO调用的本质。
  • 为什么要投反对票,至少表现出最低限度的礼貌来提供明确的理由
  • 我不是反对者,真正反对者不会收到关于您的问题的通知。
  • @Evk,谢谢,实际上我同意投反对票,只要有理由,否则滥用,我没有机制知道。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-29
  • 2023-03-15
  • 2011-09-03
相关资源
最近更新 更多