Я вызываю внешний API, который работает медленно. В настоящее время, если я не звонил в API, чтобы получить заказы, вызов можно разбить на страницы (разбиение на страницы).
Таким образом, получение заказов может быть связано с несколькими вызовами, а не с одним вызовом. Иногда каждый звонок может длиться около 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.
После 1-го «getOrders.Execute ()» есть результат разбиения на страницы, который сообщает мне, сколько там страниц данных. Я считаю, что я должен иметь возможность запускать асинхронный вызов для каждой страницы и заполнять коллекцию OrderTypeCollection. Когда все вызовы выполнены и коллекция полностью загружена, я зафиксирую в базе данных.
Я никогда раньше не выполнял асинхронные вызовы через C#, и я могу как бы следовать Async await, но я думаю, что мой сценарий выпадает из прочтения, которое я сделал до сих пор?
Вопросов:
Если кто-то может указать мне правильное направление - я буду очень признателен.
Проблема не столько в том, чтобы делать что-то асинхронно (это не ускорит), а в том, чтобы делать вызовы параллельно. Как говорит TheGeneral, у ebay нет поддержки асинхронности. Таким образом, вы можете развернуть x потоков, каждый из которых выполняет синхронный вызов для каждой страницы. Просто не создавайте 20 потоков. Spawn = количество ЦП. Или вы можете взглянуть на TPL DataFlow. Лучшее долгосрочное вложение.
@MickyD Да, извините, моя терминология, вероятно, неверна. Я ищу параллельные звонки. Я исследую ответ от генерала
Не стоит беспокоиться. Я согласен с ответом TheGeneral :)





I think I can set it up to fire off the multiple calls asynchronously but I'm not sure how to check when all tasks have been completed i.e. ready to commit to db.
Да, ты можешь разбить это
Проблема в том, что ebay не имеет метода async Task Execute, поэтому вы остаетесь с блокировкой потоковых вызовов и без шаблона асинхронного ожидания Оптимизирован для ввода-вывода. Если бы он был, вы могли бы воспользоваться конвейером TPL Dataflow, который знает async (и весело для всей семьи), вы все равно могли бы, хотя я предлагаю ванильное решение ОСАГО ...
Однако еще не все потеряно, просто вернитесь к Parallel.For и ConcurrentBag<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.
I've read somewhere that I want to avoid combining the API call and the db write to avoid locking in SQL server - Is this correct?
Дополнительные ресурсы
Для (Int32, Int32, ParallelOptions, Action)
Executes a for (For in Visual Basic) loop in which iterations may run in parallel and loop options can be configured.
Stores options that configure the operation of methods on the Parallel class.
MaxDegreeOfParallelism
Gets or sets the maximum number of concurrent tasks enabled by this ParallelOptions instance.
Represents a thread-safe, unordered collection of objects.
Я сейчас изучаю ваш ответ ... здесь есть на что посмотреть, но я уверен, что вы дали мне то, что мне нужно. Чтобы ответить на вопрос db. Да, я думаю, что мне нужно беспокоиться о тупиках? Итак, вы согласны с тем, что мне не следует писать в БД внутри самой задачи - чтобы избежать взаимоблокировок?
@GlenHong с parallel.For ожидает завершения всех потоков, следовательно, вам понадобится только одна однопоточная операция db. Однако есть много причин для тупика БД, и это полностью зависит от вашей базы данных, схемы и вашего sql.
спасибо работает отлично. Любые подсказки о том, как поступить, если в одном из этих потоков возникла ошибка, т.е. тайм-аут?
@GlenHong, вы могли бы добавить механизм повтора в блок кода для страницы. или вы можете поймать и добавить еще один concurrentbag, чтобы потом рассказать вам, какая страница вышла из строя и почему. может быть смесь обоих
Спасибо ... Я нашел ссылку docs.microsoft.com/en-us/dotnet/standard/parallel-programmin g /…, которая помогает. Я думаю, что на самом деле единственное, что может здесь произойти, - это тайм-аут или потеря соединения. А пока я просто поймаю все и сообщу пользователю, чтобы он попробовал еще раз. Далее я буду реализовывать, когда вы предлагаете повторить попытку ...
@GlenHong звучит как план и хорошая ссылка
Я мог бы просто добавить, что мне пришлось определить GetOrdersCall в Parallel, иначе я думаю, что это сбивало с толку, и количество одновременных пакетов не было всеми заказами. Определение переменных внутри цикла решило эту проблему.
Однако быстрый вопрос - у меня есть 3 магазина, и я думал запустить вложенный Parallel.for (по одному для каждого магазина), а затем вложенный (по одному для каждой страницы результатов) - возможно ли это? Я задам новый вопрос, если хотите?
@GlenHong: да, вы можете безопасно выполнять вложение, однако в любом случае система выдаст вам только определенное количество потоков, может быть, 10 для запуска. Если вы действительно хотите более высокую пропускную способность, вы можете развернуть класс Thread и управлять ими.
Да. Класс ConcurrentBag<T> можно использовать для сервера с целью одного из ваших вопросов: «Я думаю, я могу настроить его для асинхронного запуска нескольких вызовов, но я не уверен, как проверить, когда все задачи были завершены, то есть готовы. передать в db. "
Этот универсальный класс можно использовать для запуска каждой вашей задачи и ожидания завершения всех ваших задач для дальнейшей обработки. Это потокобезопасный и полезный для параллельной обработки.
Если я не ошибаюсь, это API ebay, и здесь нет асинхронных методов. также это не решает проблему асинхронного получения заказов для экономии времени
Спасибо за информацию, над ebay api еще не работал. Но проведем исследование по этому поводу.
Добро пожаловать в ebay api;) медленно как