2014-02-27 100 views
3

有一个现有的第三方Rest API可用,它将接受一组输入并返回相同的输出。 (认为​​它是必应的地理编码服务,它会接受地址和返回位置的详细信息)是否使用TPL或异步/等待

我的需求是多次调用此API(比如说500-1000)一个asp.net请求和每个调用可能需要接近500ms才能返回。

我可以想到如何做这个动作的三种方法。需要您的输入是最好的方法,以保持速度为准则。

1.一种在使用HTTP请求循环

编写一个简单的for循环和每个输入调用REST API和输出添加到结果。这到目前为止可能是最慢的。但是没有线程或上下文切换的开销。

2.使用异步和等待

使用异步和等待的机制来调用REST API。这可能是有效的,因为线程在等待REST调用返回时继续执行其他活动。我面临的问题是,根据我应该使用的建议,一直等待到最顶层的呼叫者,这在我的情况下是不可能的。不遵守它可能这里提到http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

3.使用任务并行库

使用Parallel.ForEach并使用Synchronuos API来调用平行的服务器,并使用ConcurrentDictionary导致asp.net死锁保持结果。但可能会导致线程开销

另外,让我知道是否有任何其他更好的方式来处理事情。我知道人们可能会建议跟踪每种方法的性能,但希望了解人们在之前如何解决此问题

+1

为什么不让客户端发出多个请求并将它们转发到服务器,让客户端管理异步逻辑,因为JavaScript很好地处理异步逻辑,而不是在Http Request中收集结果并将其发送回客户端。 –

+0

@AkashKava客户端通过单击按钮来完成条目。我需要调用服务api并将结果存储在每个条目的数据库中。要求JS做到这一点效率不高。 – Ramesh

+0

@AkashKava另外,并行连接到控制是受限制的,因浏览器和版本而异 – Ramesh

回答

2

首先,我认为这是值得考虑的,你没有在你的问题提一些问题:

  • 500-1000 API调用听起来不少。没有办法避免这种情况吗? API不具有某种批量查询功能吗?或者不能下载他们的数据库并在本地查询它? (像Wikimedia或Stack Exchange这样的更开放的组织通常会支持这一点,但像微软或Google这样的更加封闭的组织通常不会这样)。

    如果这些选项不可用,那么至少考虑某种缓存,如果对你有意义。

  • 在ASP.NET中同时允许同一服务器发出的并发请求数量默认只有10个。如果您想要发出更多并发请求,则需要设置ServicePointManager.DefaultConnectionLimit

  • 发出这么多请求可能会被服务提供商视为滥用,并可能导致您的IP被阻止。确保提供商可以使用这种用法。现在

,你的实际问题:我认为最好的选择是使用async - await,即使你不能使用它的所有道路。通过在每个await(这是正确的解决方案)上使用ConfigureAwait(false)或使用类似Task.Run(() => /* your async code here */).Wait()的内容来避免ASP.NET上下文(这是简单的解决方案),您可以避免死锁。

使用类似Parallel.ForEach()的东西并不好,因为它不必要地浪费了ThreadPool线程。

如果你跟async一起去,你也应该考虑节流。一个简单的方法是通过使用SemaphoreSlim

+0

谢谢@scivk - 没有批量查询/数据库访问。肯定会考虑缓存值。并发连接到同一台服务器对我来说是新的。我会更详细地看。我相信第三方已经白色列出了我们服务器的IP地址。非常丰富。谢谢 – Ramesh

+0

你能解释一下在异步情况下还需要节流吗? – Ramesh

+0

@好吧,设置'ServicePointManager.DefaultConnectionLimit'会影响你的整个应用程序。因此,如果您想在应用程序的不同部分使用不同数量的并发连接,则调节可能是正确的方法。 – svick

1

异步/等待只是syntactic sugar for TPL

此外,它不是强制性的,您使用“等待到最上面调用”,异步方法返回任务,您可以调用.Result.Wait而不是等待,因此必须使您的方法异步。

至于Paralell.ForEach“导致线程开销”,这并不是真正的问题,因为Paralell.ForEach makes use of the ThreadPool

使用异步/等待或Paralell.ForEach,你会没事的任何人。

考虑使用Semaphore限制并发呼叫的数量。公共REST的API通常会开始拒绝请求,如果你超过一定的数量在快速连续

+0

我同意使用'async' /'await'通常是要走的路,但是如果我可能会挑剔:'async'只是实际返回一个'Task'时的TPL语法糖。 “等待”与TPL根本没有内在联系;您可以等待遵循特定模式的任何内容。 –

+0

我会说不必要地使用可能的数百个线程是一个问题。特别是在ASP.NET应用程序中。 – svick

+1

@Rui - 引用来自链接“通过调用Task.Wait或Task.Result阻止异步代码通常是一个坏主意,对于将脚本”浸入异步编程“的程序员来说,这是一个特别常见的问题,他们的应用程序的一小部分,并将其包装在一个同步API中,以便应用程序的其余部分与变化隔离。不幸的是,它们遇到了死锁问题“ – Ramesh

3

最好的解决方案是使用asyncawait电话,但在这种情况下,你必须把它async一路攀升调用堆栈到控制器的动作。

for循环保持所有的顺序和同步,所以它肯定是最慢的解决方案。Parallel会针对每个请求阻止多个线程,这会对您的可伸缩性产生负面影响。

由于操作是基于I/O的(调用REST API),所以async是最自然的选择,应该提供这些选项的最佳整体系统性能。

+0

为什么不使用'Parallel'好点。谢谢 – Ramesh