【问题标题】:Long API Call - Async calls the answer?长 API 调用 - 异步调用答案?
【发布时间】:2018-10-30 15:35:52
【问题描述】:

我正在调用一个很慢的外部 API。目前,如果我有一段时间没有调用 API 来获取一些订单,则可以将调用分解为页面(分页)。

因此,获取订单可能是多次调用而不是 1 次调用。有时每次通话可能需要 10 秒左右,因此总共可能需要大约一分钟,这太长了。

        GetOrdersCall getOrders = new GetOrdersCall();
        getOrders.DetailLevelList.Add(DetailLevelCodeType.ReturnSummary);
        getOrders.CreateTimeFrom = lastOrderDate;
        getOrders.CreateTimeTo = DateTime.Now;

        PaginationType paging = new PaginationType();
        paging.EntriesPerPage = 20;
        paging.PageNumber = 1;

        getOrders.Pagination = paging;

        getOrders.Execute();

        var response = getOrders.ApiResponse;
        OrderTypeCollection orders = new OrderTypeCollection();

        while (response != null && response.OrderArray.Count > 0)
        {
            eBayConverter.ConvertOrders(response.OrderArray, 1);

            if (response.HasMoreOrders)
            {
                getOrders.Pagination.PageNumber++;
                getOrders.Execute();

                response = getOrders.ApiResponse;
                orders.AddRange(response.OrderArray);
            }
        }

这是我上面代码的摘要... getOrders.Execute() 是 api 触发的时间。

在第一个“getOrders.Execute()”之后有一个分页结果,它告诉我有多少页数据。我的想法是我应该能够为每个页面启动异步调用并填充 OrderTypeCollection。当所有调用都完成并且集合完全加载后,我将提交到数据库。

我以前从未通过 c# 进行过异步调用,我可以遵循 Async await,但我认为我的场景超出了我目前所做的阅读范围?

问题:

  1. 我想我可以将其设置为异步触发多个调用,但我不确定如何检查所有任务何时完成,即准备好提交到 db。
  2. 我在某处读到过,我希望避免将 API 调用和 db 写入结合起来以避免锁定 SQL Server - 这是正确的吗?

如果有人能指出我正确的方向 - 将不胜感激。

【问题讨论】:

  • 欢迎使用 ebay api ;) 慢
  • 问题不在于异步执行(这不会使其更快),而是并行调用。正如 TheGeneral 所说,ebay 无论如何都没有异步支持。因此,您可以启动 x 个线程,每个线程每页执行一个同步调用。只是不要产生 20 个线程。产生 = CPU 计数。或者您可能想看看 TPL DataFlow。更好的长期投资。
  • @MickyD 是的,抱歉,我的术语可能不正确。我正在寻找并行进行这些调用。我正在调查将军的回答
  • 不用担心。我同意将军的回答:)

标签: c# async-await


【解决方案1】:

我想我可以将其设置为异步触发多个调用 但我不确定如何检查所有任务何时完成,即 准备提交到 db。

是的,你可以分手

问题是ebay 没有async Task Execute 方法,因此您只能使用阻塞线程调用并且没有IO 优化 异步等待模式。如果有,您可以利用 async 感知的 TPL 数据流 管道(全家人都可以玩),尽管我建议使用普通的 TPL 解决方案...

但是,一切都没有丢失,只需退回到 Parallel.ForConcurrentBag<OrderType>

示例

var concurrentBag = new ConcurrentBag<OrderType>();


// make first call
// add results to concurrentBag
// pass the pageCount to the for
int pagesize = ...;

Parallel.For(1, pagesize,
   page =>
      {
         // Set up
         // add page
         // make Call 

         foreach(var order in getOrders.ApiResponse)
            concurrentBag.Add(order);
      });

// all orders have been downloaded
// save to db

注意:你可以配置MaxDegreeOfParallelism,也许设置为50,虽然你给它多少并不重要,任务计划程序不是会积极地给你线程,最初可能有 10 个左右,然后慢慢增长。

另一种方法是创建自己的任务计划程序,或者使用老式的Thread 类启动自己的线程


我在某处读到过我想避免将 API 调用与 db 写入以避免锁定 SQL 服务器 - 这是正确的吗?

  • 如果您的意思是在慢速 DB 插入中锁定,请使用 Sql Bulk Insert 和更新工具。
  • 如果您的意思是像 DB 死锁错误消息中那样锁定,那么这是完全不同的事情,值得自己提出问题

其他资源

For(Int32, Int32, ParallelOptions, Action)

执行一个 for(在 Visual Basic 中为 For)循环,其中可以运行迭代 可以配置并行和循环选项。

ParallelOptions Class

存储配置并行方法操作的选项 类。

MaxDegreeOfParallelism

获取或设置由此启用的最大并发任务数 ParallelOptions 实例。

ConcurrentBag Class

表示线程安全、无序的对象集合。

【讨论】:

  • 我现在正在调查您的回复...这里有很多东西要看,但我相信您已经给了我我需要的东西。回答 db 问题。是的,我认为我需要担心的是僵局?所以你同意我不应该在任务本身中写入数据库——以避免死锁?
  • @GlenHong 和parallel.For 它等待所有线程完成,因此,事后您只需要一个单线程数据库操作。然而,导致数据库死锁的原因有很多,它完全取决于您的数据库、架构和您的 sql
  • 谢谢你完美的工作。如果这些线程之一出现错误(即超时),有关如何处理的任何提示?
  • @GlenHong 您能否在页面的代码块中添加重试机制。或者您可以捕获并添加另一个并发包,然后告诉您哪个页面失败以及原因。也许两者兼而有之
  • 谢谢...我找到了这个链接docs.microsoft.com/en-us/dotnet/standard/parallel-programming/… 这有帮助。我认为实际上这里唯一可能发生的事情可能是超时或失去连接。现在我只做一个全面了解并报告给用户再试一次。进一步,我将按照您的建议重试...
【解决方案2】:

是的ConcurrentBag&lt;T&gt; Class 可用于解决您的一个问题的目的,即:“我想我可以将其设置为异步触发多个调用,但我不确定如何检查所有任务的时间已完成,即准备好提交到 db。"

这个通用类可用于运行您的每个任务并等待您的所有任务完成以进行进一步处理。它是线程安全的,对并行处理很有用。

【讨论】:

  • 如果我没记错的话,这是 ebay api,并且没有异步方法。这也不能解决异步获取订单以节省时间的问题
  • 感谢您的信息,我还没有研究过ebay api。但会对此进行研究。
猜你喜欢
  • 2018-01-30
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 2022-01-19
  • 2017-10-28
  • 2020-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多