前言
在上次的《差距50倍!为什么Web API第一次执行这么慢?》文章中,我们发现了部分耗时比较大的方法:
-
Microsoft.AspNetCore.Mvc.Infrastructure.ActionInvokerFactory.CreateInvoker - 30.15ms
查看源代码,ActionInvokerFactory使用的是Singleton生命周期:
services.TryAddSingleton();
而在构造函数中,会执行排序方法:
public ActionInvokerFactory(IEnumerable 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的执行时间确实大幅减少:
未预热预热后初次执行时间100ms21ms 实现但是,不太好每次启动服务后,都先手工访问一下预热接口。
我们可以设置成,在启动后,自动访问一下预热接口。
利用应用程序生存期事件,可以达到这一目的。
修改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预热。