Параллельные звонки на внешние сервисы - PullRequest
0 голосов
/ 09 июня 2018

В настоящее время я работаю над проектом по созданию интеграции между существующим веб-сайтом ASP.Net MVC и службой хостинга файлов, которую использует моя компания.Типичный вариант использования:

  1. Пользователь запрашивает один или несколько файлов
  2. Контроллер выполняет один вызов на файл для API файлового хоста
  3. Хост файла возвращаетданные файла в контроллер
  4. Контроллер возвращает результат файла

Служба хостинга может обрабатывать параллельные вызовы, и я обнаружил, что выполнение каждого вызова API внутри задачи (см.пример ниже) приводит к довольно резким улучшениям.

private void RetrieveDocuments(DocumentIdentifier[] identifiers, List<FileHostResult> results)
{
    var tasks = identifiers.Select(x => RetrieveDocument(results, x)).ToArray();
    Task.WaitAll(tasks);
}

private Task RetrieveDocument(List<FileHostResult> results, DocumentIdentifier x)
{
    return Task.Run(() =>
    {
        var result = GetFileHostResultFromFileHost(x.ExternalIdentifier);
        lock (results)
        {
            results.Add(result);
        }
    });
}

Мой вопрос заключается в том, есть ли лучший способ сделать это или есть какие-нибудь потенциальные ловушки, с которыми я могу столкнуться?(например, блокировка ресурсов сервера и т. д.).

РЕДАКТИРОВАТЬ 1: Я не опубликовал код для GetFileHostResultFromFileHost, потому что у меня нет прав для его изменения.В основном это вызов метода, реализованный в библиотеке, который я не могу изменить.

РЕДАКТИРОВАТЬ 2: Чтобы уточнить.Моя главная задача - не наносить вред текущему опыту пользователей на сайте.В связи с этим я хочу убедиться, что одновременное выполнение задач из ASP.net mvc не приведет к блокировке сайта.

Ответы [ 2 ]

0 голосов
/ 09 июня 2018

Для этого вы должны использовать Microsoft Reactive Framework.Он идеально подходит для такой обработки.

Вот код:

IObservable<FileHostResult> query =
    from i in identifiers.ToObservable()
    from r in Observable.Start(() => GetFileHostResultFromFileHost(i.ExternalIdentifier))
    select r;

IList<FileHostResult> results = query.ToList().Wait();

Вот и все.Он правильно распределяет код по оптимальному количеству потоков.

Если вам нужен ожидаемый код, вы можете сделать это:

IObservable<FileHostResult> query =
    from i in identifiers.ToObservable()
    from r in Observable.Start(() => GetFileHostResultFromFileHost(i.ExternalIdentifier))
    select r;

IList<FileHostResult> results = await query.ToList();

Это действительно очень просто и легко кодировать.

NuGet "System.Reactive", а затем добавьте using System.Reactive.Linq; к своему коду.

0 голосов
/ 09 июня 2018

Трудно дать отличный совет, не видя остальной части исходного кода.Но исходя из того, что я вижу, я бы предложил такой подход:

private void RetrieveDocuments(DocumentIdentifier[] identifiers, List<FileHostResult> results)
{
    results.AddRange(identifiers.AsParallel().Select(x => RetrieveDocument(x)));
}

private FileHostResult RetrieveDocument(DocumentIdentifier x)
{
    var result = GetFileHostResultFromFileHost(x.ExternalIdentifier);
    return result;
}

Преимущества этого подхода:

  • Нет явного использования Task.Run - пусть AsParallel позаботьтесь об этом за вас.
  • Нет необходимости блокировать список results - пусть AsParallel и Select позаботятся об этом за вас

Вы также можете увеличить максимальное количество соединений, к которым у вас есть доступ.

Хотя, честно говоря, я думаю, что вам следует рассмотреть подходы, которые не требуютновые Task с вообще - вероятно, с использованием Async вызовов загрузки http, которые вы можете запускать параллельно без издержек потока.

...