ASP.NET MVC4 页面在 Microsoft Azure 上加载速度极慢
ASP.NET MVC4 pages loading extremely slow on Microsoft Azure
在将 ASP.NET MVC4 应用程序部署到 Microsoft Azure 后,我目前正在处理非常烦人的性能问题。重新启动应用程序后(或几分钟不活动后),某些页面首次加载大约需要 15 秒。在此之后,即使清除客户端缓存,这些页面也将在大约 2 秒内加载(这仍然需要改进,但比 15 秒要好得多,这是一个巨大的用户体验杀手)。
这是我目前已经尝试过的方法:
- 我梳理了 Global.asax 中的每个事件,以便用虚拟代码替换与数据库相关的代码
- 将发布设置更改为:«发布期间预编译»(将所有输出合并到一个程序集中)
- 根据 Azure 的“应用性能分析”,我的应用被认为是“健康的”
- «Always On» 在门户的应用程序设置中激活(定价层为 B1 [基本,1 个核心,1.75GB RAM],因此我假设永远在线开关不会被共享运行时配置)
由于上面的 none 解决了我的问题,我尝试使用 Quartz.NET 编写一个 keppalive-Job,它定期请求这样的网站:
new System.Net.WebClient().OpenRead("https://foo.azurewebsites.net");
(在我的例子中,我认为效果或多或少与在 Web.Config 内声明初始化页面相同 here,不是吗?)
结果:有效,但仅针对此特定 URL。维护每条可能路线的列表绝对不是要走的路。
你有没有处理过这个问题?非常感谢您的意见!
自从我在托管在 azure 上的组织工作以来已经有一段时间了,但他们曾经有一个选项,您可以配置标记为 "Always On" 这实际上等同于设置空闲超时在应用程序池上归零并保持您的应用程序加载到内存中。否则在没有流量的情况下,您的站点将在第一次点击时被卸载并强制执行 JIT(有点像初始部署后)。
我读到其他人向他们的网站创建模拟 http 请求,以保持流量持续增长,并避免在他们订阅了不提供 "always on" 选项的低价套餐时从内存中卸载.
编辑:这是另一个 SO post 解决它:App pool timeout for azure web sites
除了接受的答案之外,我还想详细解释一下我最终是如何处理这个问题的。也许这可能会在将来帮助其他人。
我编写了一些代码,通过简单地在关键站点上触发 Web 请求来在启动时预热应用程序(我的经验是,这需要对每个页面完成。仅预热根地址是不够的)。
private void WarmUp()
{
var baseUrl = "https://foo.azurewebsites.net";
/**
* Requests to protected pages need to be authenticated and authorized, otherwise the JIT-Compile won't work
* For ASP.NET apps that are using FormsAuthentication just send a POST-request as you normally would do using a html form,
* then grab the cookie you get in the response and pass it to the subsequent requests
* */
var email = "warmup.worker@foo.com";
var password = "verysecure";
var cookies = new CookieContainer();
var webRequest = WebRequest.Create($"{baseUrl}/Account/Login") as HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookies;
var requestWriter = new StreamWriter(webRequest.GetRequestStream());
requestWriter.Write($"user={email}&password={password}");//Small example for brevity. Don't forget to extract the requestVerificationToken in production :-)
requestWriter.Close();
webRequest.GetResponse().Close();
var urls = new[] {
baseUrl,
$"{baseUrl}/Home/Contact",
$"{baseUrl}/Protected/Stuff",
//...
};
foreach (var url in urls)//trigger web-requests
{
webRequest = WebRequest.Create(url) as HttpWebRequest;
webRequest.CookieContainer = cookies;
webRequest.GetResponse().Close();
}
}
这会在应用 starts/after 部署时为我们触发耗时的 JIT 操作,从而防止我们的用户在请求页面时体验糟糕的性能。如果您的应用程序的定价计划是 S1 或更高,您可以使用 deployment slots 进一步提高性能。它们允许您甚至在部署到生产环境之前预热应用程序。
就我而言(保留定价层,B1),我认为在 Application_Start 内 运行 上面的代码一次就足够了,因为“Always on”应该会阻止我的应用程序的工作进程如果站点在设定的时间段内没有任何流量,则会自动关闭(如果您的应用程序 运行 正在共享资源上,请确保使用 Cron 作业或类似的东西让您的应用程序保持活动状态)。
在将 ASP.NET MVC4 应用程序部署到 Microsoft Azure 后,我目前正在处理非常烦人的性能问题。重新启动应用程序后(或几分钟不活动后),某些页面首次加载大约需要 15 秒。在此之后,即使清除客户端缓存,这些页面也将在大约 2 秒内加载(这仍然需要改进,但比 15 秒要好得多,这是一个巨大的用户体验杀手)。
这是我目前已经尝试过的方法:
- 我梳理了 Global.asax 中的每个事件,以便用虚拟代码替换与数据库相关的代码
- 将发布设置更改为:«发布期间预编译»(将所有输出合并到一个程序集中)
- 根据 Azure 的“应用性能分析”,我的应用被认为是“健康的”
- «Always On» 在门户的应用程序设置中激活(定价层为 B1 [基本,1 个核心,1.75GB RAM],因此我假设永远在线开关不会被共享运行时配置)
由于上面的 none 解决了我的问题,我尝试使用 Quartz.NET 编写一个 keppalive-Job,它定期请求这样的网站:
new System.Net.WebClient().OpenRead("https://foo.azurewebsites.net");
(在我的例子中,我认为效果或多或少与在 Web.Config 内声明初始化页面相同 here,不是吗?)
结果:有效,但仅针对此特定 URL。维护每条可能路线的列表绝对不是要走的路。
你有没有处理过这个问题?非常感谢您的意见!
自从我在托管在 azure 上的组织工作以来已经有一段时间了,但他们曾经有一个选项,您可以配置标记为 "Always On" 这实际上等同于设置空闲超时在应用程序池上归零并保持您的应用程序加载到内存中。否则在没有流量的情况下,您的站点将在第一次点击时被卸载并强制执行 JIT(有点像初始部署后)。
我读到其他人向他们的网站创建模拟 http 请求,以保持流量持续增长,并避免在他们订阅了不提供 "always on" 选项的低价套餐时从内存中卸载.
编辑:这是另一个 SO post 解决它:App pool timeout for azure web sites
除了接受的答案之外,我还想详细解释一下我最终是如何处理这个问题的。也许这可能会在将来帮助其他人。 我编写了一些代码,通过简单地在关键站点上触发 Web 请求来在启动时预热应用程序(我的经验是,这需要对每个页面完成。仅预热根地址是不够的)。
private void WarmUp()
{
var baseUrl = "https://foo.azurewebsites.net";
/**
* Requests to protected pages need to be authenticated and authorized, otherwise the JIT-Compile won't work
* For ASP.NET apps that are using FormsAuthentication just send a POST-request as you normally would do using a html form,
* then grab the cookie you get in the response and pass it to the subsequent requests
* */
var email = "warmup.worker@foo.com";
var password = "verysecure";
var cookies = new CookieContainer();
var webRequest = WebRequest.Create($"{baseUrl}/Account/Login") as HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookies;
var requestWriter = new StreamWriter(webRequest.GetRequestStream());
requestWriter.Write($"user={email}&password={password}");//Small example for brevity. Don't forget to extract the requestVerificationToken in production :-)
requestWriter.Close();
webRequest.GetResponse().Close();
var urls = new[] {
baseUrl,
$"{baseUrl}/Home/Contact",
$"{baseUrl}/Protected/Stuff",
//...
};
foreach (var url in urls)//trigger web-requests
{
webRequest = WebRequest.Create(url) as HttpWebRequest;
webRequest.CookieContainer = cookies;
webRequest.GetResponse().Close();
}
}
这会在应用 starts/after 部署时为我们触发耗时的 JIT 操作,从而防止我们的用户在请求页面时体验糟糕的性能。如果您的应用程序的定价计划是 S1 或更高,您可以使用 deployment slots 进一步提高性能。它们允许您甚至在部署到生产环境之前预热应用程序。
就我而言(保留定价层,B1),我认为在 Application_Start 内 运行 上面的代码一次就足够了,因为“Always on”应该会阻止我的应用程序的工作进程如果站点在设定的时间段内没有任何流量,则会自动关闭(如果您的应用程序 运行 正在共享资源上,请确保使用 Cron 作业或类似的东西让您的应用程序保持活动状态)。