it-swarm.com.ru

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

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

Рабочий процесс:

Приложение пользовательского интерфейса → Конечная точка Web API (контроллер) → Метод вызова на уровне сервиса 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. Пожалуйста, поделитесь своими мыслями.

91
arp

Я не очень уверен, повлияет ли это на производительность моего 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, запросы к базе данных и т.д.) И сделайте их асинхронными на низшем уровне (Service.ProcessAsync). Затем позвольте async накапливаться, делая действия контроллера асинхронными в качестве последнего шага.

И ни при каких обстоятельствах вы не должны использовать Task.Run в этом сценарии.

166
Stephen Cleary

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

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

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

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

       return list;
    }
}

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

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

9
Richard

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

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

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

0
James Wierzba