我正在尝试在我的 Web API 项目中使用 ASP.NET 的 async/await
功能。我不太确定它是否会对我的 Web API 服务的性能产生任何影响。请在下面找到我的应用程序的工作流程和示例代码。
工作流程:
UI 应用 → Web API 端点(控制器) → 在 Web API 服务层调用方法 → 调用另一个外部 Web 服务。 (这里我们有数据库交互等)
控制器:
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
的正确用法。请分享你的想法。
我不太确定它是否会对我的 API 的性能产生任何影响。
请记住,服务器端异步代码的主要好处是可扩展性。它不会神奇地使您的请求运行得更快。我在我的 article on async
ASP.NET 中介绍了几个“我应该使用 async
”的注意事项。
我认为您的用例(调用其他 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
。
这是正确的,但也许没有用。
由于没有什么可等待的——没有对可以异步操作的阻塞 API 的调用——那么您正在设置结构来跟踪异步操作(这有开销),但随后没有使用该功能。
例如,如果服务层使用支持异步调用的实体框架执行数据库操作:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
using (db = myDBContext.Get()) {
var list = await db.Countries.Where(condition).ToListAsync();
return list;
}
}
您将允许工作线程在查询数据库时执行其他操作(从而能够处理另一个请求)。
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
的好处。
Service
不是异步方法,并且不会在 Service
调用本身中等待。我可以理解他的观点,但我认为它不适用于这里。
Task.Run
。这种方法会消除 async
/await
的所有好处,并且实际上在负载下的性能比仅仅同步调用更差。
不定期副业成功案例分享