如何预热Web API,减少初次执行时间
前言
- 在上次的《差距50倍!为什么Web API第一次执行这么慢?》文章中,我们发现了部分耗时比较大的方法:
Microsoft.AspNetCore.Mvc.Infrastructure.ActionInvokerFactory.CreateInvoker - 30.15ms
- 查看源代码,ActionInvokerFactory使用的是Singleton生命周期:
services.TryAddSingleton<IActionInvokerFactory, ActionInvokerFactory>();
而在构造函数中,会执行排序方法:
public ActionInvokerFactory(IEnumerable<IActionInvokerProvider> actionInvokerProviders)
{
_actionInvokerProviders = actionInvokerProviders.OrderBy(item => item.Order).ToArray();
}
- 我想,这应该是初次执行时间较长的部分原因。
思路
使用Singleton生命周期的类,在应用的生存期内仅创建一次,相当于静态类。
如果我们在执行Web API之前,就使用过了ActionInvokerFactory,那么在第一次执行Web API时就不会再次初始化它,应该可以减少初次执行时间。
验证
- 创建一个WarmController,代码非常简单:
[ApiController]
[Route("[controller]")]
public class WarmController : ControllerBase
{
[HttpGet]
public string Get()
{
return "OK";
}
}
- 首先访问Warm接口,再访问WeatherForecast接口,发现初次访问WeatherForecast的执行时间确实大幅减少:
未预热 | 预热后 | |
---|---|---|
初次执行时间 | 100ms | 21ms |
实现
- 但是,不太好每次启动服务后,都先手工访问一下预热接口。
- 我们可以设置成,在启动后,自动访问一下预热接口。
- 利用应用程序生存期事件,可以达到这一目的。
修改Startup.cs,代码如下:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime)
{
...
lifetime.ApplicationStarted.Register(OnAppStarted);
}
public void OnAppStarted()
{
var url = Configuration[WebHostDefaults.ServerUrlsKey];
var warm = url.Split(';')[0] + "/warm";
new HttpClient().GetAsync(warm).Wait();
}
- 启动后访问WeatherForecast接口,发现确实已经预热过了。
结论
在本文中,我们利用了ASP.NET Core应用程序生存期事件,实现了Web API预热。