Эффективно использовать async / await с помощью ASP.NET Web API



Я пытаюсь использовать async/await особенность ASP.NET в моем проекте Web API. Я не очень уверен, будет ли это иметь какое-либо значение в производительности моей службы Web API. Ниже приведен рабочий процесс и пример кода из моего приложения.



График Работы:



UI Application → Web API endpoint (controller) → метод вызова на уровне службы Web API → вызов другой внешней веб-службы. (Здесь мы имеем взаимодействия с БД, так далее.)



:



public async Task<IHttpActionResult> GetCountries()
{
var allCountrys = await CountryDataService.ReturnAllCountries();

if (allCountrys.Success)
{
return Ok(allCountrys.Domain);
}

return InternalServerError();
}


Сервис Слой:



public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");

return Task.FromResult(response);
}


Я проверил приведенный выше код и работает. Но я не уверен, что это правильное использование async/await. Пожалуйста, поделитесь своими мыслями.

566   4  

4 ответов:

Я не очень уверен, будет ли это иметь какое-либо значение в производительности моего API.

имейте в виду, что основным преимуществом асинхронного кода на стороне сервера масштабируемость. Это не волшебным образом заставит ваши запросы работать быстрее. Я покрываю несколько "должен ли я использовать async " соображения по моему статью async ASP.NET.

Я думаю, что ваш вариант использования (вызов других API) хорошо подходит для асинхронного код, просто имейте в виду, что "асинхронный" не означает "быстрее". Лучший подход-сначала сделать свой UI отзывчивый и асинхронный; это сделает ваше приложение чувствовать быстрее, даже если это немного медленнее.

насколько код идет, это не асинхронные:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
  var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
  return Task.FromResult(response);
}

вам понадобится действительно асинхронная реализация, чтобы получить преимущества масштабируемости async:

public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

или (если ваша логика в этом методе на самом деле это просто проход):

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

обратите внимание, что легче работать "изнутри", а не "снаружи", как это. Другими словами, Не начинайте с действия асинхронного контроллера, а затем принудительно выполняйте асинхронные методы. Вместо этого определите естественно асинхронные операции (вызов внешних API, запросов к базе данных и т. д.) и сделайте их асинхронными в низкий

это правильно, но, возможно, не полезно.

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

например, если сервисный уровень выполнял операции БД с Entity Framework, которая поддерживает асинхронные вызовы:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    using (db = myDBContext.Get()) {
      var list = await db.Countries.Where(condition).ToListAsync();

       return list;
    }
}

вы бы позволили сделать рабочий поток что-то еще, пока БД был запрошен (и, таким образом, может обрабатывать другой запрос).

Await имеет тенденцию быть чем-то, что должно идти до конца: очень трудно ретро-вписаться в существующую систему.

вы не используете async / await эффективно, потому что поток запроса будет заблокирован при выполнении синхронно метод ReturnAllCountries()

поток, назначенный для обработки запроса, будет просто ждать, пока ReturnAllCountries() это работает.

если вы можете реализовать ReturnAllCountries() чтобы быть асинхронным, то вы увидите преимущества масштабируемости. Это связано с тем, что поток может быть выпущен обратно в пул потоков .NET для обработки другого запроса, в то время как ReturnAllCountries() выполняется. Это позволит вашей службе иметь более высокую пропускную способность, более эффективно используя потоки.

Я бы изменил ваш уровень обслуживания на:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    return Task.Run(() =>
    {
        return _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
    }      
}

как у вас есть, вы все еще работаете ваш _service.Process вызов синхронно,и получение очень мало или никакой выгоды от ожидания его.

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

Comments

    Ничего не найдено.