【问题标题】:Azure Web App (ASP.NET MVC) becomes cold every ten minutes and takes +10-20s to loadAzure Web App (ASP.NET MVC) 每十分钟变冷一次,加载需要 +10-20 秒
【发布时间】:2018-10-11 11:01:37
【问题描述】:

我对 Azure Web 应用程序有一个非常奇怪的问题,对此我感到非常沮丧。

我们体验到我们的应用在使用时非常快速且响应迅速,但是,如果我们在大约十分钟内不使用它,它就会有一个非常冷的启动(约 10-20 秒)。这种冷启动只有在涉及数据库时才会发生。有点像我们发布网络应用的时候。

我们的尝试

使用 Azure 中的 Application Insights,我们每 5 分钟设置一次 ping:

异常值总是由我的部署引起的(现在不使用部署槽)。然而,这个登录页面并没有调用我们的数据库,所以我们在这些数据中看不到“冷启动”。

应用程序设置应该是可靠的。我们的网络应用托管在北欧,Always on

我们刚刚将整个设置移至新的资源组/应用服务计划,以确保我们的问题与我们的其他应用纠缠在一起。新的应用服务计划是Standard 1 small,应该没有问题。看我们的消费我并不担心,甚至可能会尝试一个较小的服务,我会在解决我们的问题后做:

我们的 SQL 数据库也托管在北欧(检查位置十亿次,因为我以前犯过这个错误)。

就像应用服务一样,我们选择了“太大”的硬件来确保不会导致问题(标准 S0:10 个 DTU)。使用率低得离谱:

我们确实使用持续部署(Azure 菜单中的Deployment options),但看看部署,它不应该持续部署一些东西:

令人沮丧的是,该应用在运行时反应灵敏。当它“温暖”时,每个页面都会在几秒钟内加载,就像我在我们的网络应用上显示的平均响应时间一样:

但是当我们(或我们的用户!)使用我们的应用程序时,这些数字完全是错误的。在这里,我们体验到第一次加载通常是 +10-20 秒。

有人知道吗?有什么提示吗?你不知道我会多么感激。

编辑和更新 1:

我决定设置更多测试。我现在已经设法通过调用另一个页面来获取显示我们问题的真实数据。具有讽刺意味的是,这个页面不调用数据库,所以虽然我认为这是一个数据库问题,但它看起来不像这样。在此处查看挑战(趋势持续 +24 小时)。

奇怪的是,它精确到了大约 10 秒。而且趋势似乎不是每 10-20 分钟一次,而是更接近每 5 分钟一次——它们之间的间隔完全相同:

编辑和更新 2:

我一直在挖掘更多。结果发现有几个非常有趣的见解:来自编辑 1 的“慢”11 秒调用仅来自美国东部和一个端点 (http://prntscr.com/jcv69w),并且

我发现的最有趣的是:

应用程序本身没有任何缓存。我使用实体框架,我假设它使用了一些缓存,但仅此而已。

我登录了我们的应用程序,并在 Chrome 中四处点击。我发现,我已经访问过的页面是即时显示的(使用数据库中的数据),但是如果我打开一个新页面,它会加载缓慢。我第一次打开页面时,似乎有些实体被缓存了。

然后我尝试在新浏览器中打开该应用程序。如果我打开之前在 Chrome 中打开的页面,它会立即打开。如果我打开一个我之前没有点击过的新页面,它的加载时间约为 10 秒。

我现在最好的猜测是我使用的实体框架由于某种原因出现了问题。

编辑 3:

刚刚添加了一个赏金,并且正在设置大量日志记录。我已经添加了 MiniProfiler,但无法让它在生产中工作(仅在本地请求中显示)。

我还为 Application_StartApplication_BeginRequestApplication_EndRequest 添加了登录 global.asax 以查看其中的一些内容和状态。很快就会更新调查结果。

编辑 4:

所以现在我有了第一个有趣的数字。该应用程序没有被回收。 Application_Start 只被调用一次。

我可以通过登录EndRequestBeginRequest 来查看时差。我可以看到这两个之间有多个调用需要超过 +15 秒......但是当网站温暖时,它需要 ~0.5-2 秒,具体取决于页面。所以在请求的开始和结束之间发生了一些非常奇怪的事情。进一步调试!

编辑 5:

让 MiniProfiler 工作。以下是慢速加载的示例(约 15 秒):

我的下一步是添加实体框架跟踪,甚至为线路调用添加更多线路。我在数据库上赚钱!

编辑 6:

Okidoki,我错了。渲染方法很慢 - 而不是数据库!我不知道如何调试这个... 谷歌!

编辑 7:

是时候再更新一次了。状态是:什么都没有解决。

所以我尝试了很多东西:

1) 我尝试禁用所有类型的缓存 (Prevent Caching in ASP.NET MVC for specific actions using an attribute),但我的行为相同。第一次加载?慢。下一个负载?快速地。等待 5-10 分钟,同样的行为没有解决。

2) 我的 startup.auth 文件中有一些自定义内容,延迟了 5 分钟。已移除。不是问题。

3) 我使用自定义属性进行授权。我删除了它。

4) 我更新了我的实体框架实现以使其在每个请求中都能正常工作

我真的很沮丧。我的下一步是:

A) 尝试制作 5-10 个版本的同一页面(没有 _layout,有布局,有数据库,没有数据库,有依赖注入,没有......所有这些东西),所以看看我是否能找到一个模式。

B) 尝试将主机移至虚拟机,看看是否能解决问题

编辑 8 - 添加新遗物:

我现在添加了新遗物。以下是两件非常可怕的事情(我发现并重现了错误!):

在前端方面(New Relic 的浏览器部分),两次启动之间有约 15 秒的间隔:

http://prntscr.com/jevgeghttp://prntscr.com/jevgix 之间没有任何区别。

【问题讨论】:

  • github.com/projectkudu/kudu/issues/2583 这是我们在部署槽时遇到的关于冷启动的问题。 TL:DR 尝试仅按照blogs.msdn.microsoft.com/benjaminperkins/2017/11/30/… 的说明使应用程序 HTTPS
  • 在此之前,请检查应用洞察 ping URL 和您的用户使用的 URL 是 http 还是 https。我猜一个是http,另一个是https。胡思乱想
  • @GuruCharan94 该应用程序已经仅支持 HTTPS,应用程序洞察力调用 http 版本。不幸的是:)
  • 如果 HTTPS only 设置为 ON,则 appinsights ping 应自动重定向到 HTTPS 链接。但是,无论应用程序洞察如何,此“始终开启”功能都应该处理冷启动。目前想不出任何其他解决方案...
  • 可以给我们看一下模型和查看代码吗?

标签: asp.net asp.net-mvc azure azure-web-app-service devops


【解决方案1】:

我有几个可能的答案。

实体框架代码优先/数据库初始化: 如果您将代码优先设置与迁移和可能的种子数据一起使用,则这些事情中的每一个都可能导致一些“预热”问题。

特别是如果您没有在应用程序启动时初始化数据库,这意味着您第一次访问数据库就是它被初始化的时候。

实体框架版本: 实体框架本身在 5 和 6.x 中也有很多性能改进,其中一些也与冷启动和热启动速度有关。

视图未预编译: 如果每次第一次点击新页面(视图)时页面加载缓慢(例如部署后),那么后续加载就可以了。 这可能是因为页面没有编译,如果是这样,我可以详细说明。

回收 听起来您在应用程序回收时遇到了这些问题,并且它不是自动初始化(这就是您受到冷击的原因)我见过的最糟糕的性能问题通常与实体框架和预编译相关。但两者都可以轻松修复。但确保应用程序“始终运行”并在回收后自行初始化也可以确保没有用户受到这种冷击。

更新: 由于它与视图相关,我可以提供一个我发现非常有用的解决方案。 安装 RazorGenerator.Mvc Nuget 包。并且将此引擎添加为第一个引擎将确保您使用已编译的视图。

App_Start 中,您可以创建一个名为RazorGeneratorMvcStart.cs 的文件,其内容如下:

using RazorGenerator.Mvc;

[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(MyNamespace.RazorGeneratorMvcStart), "Start")]

namespace MyNamespace {
    public static class RazorGeneratorMvcStart {
        public static void Start() {
            ViewEngines.Engines.Insert(0, new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly));

            VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
        }
    }
}

对于喜欢实时替换视图的用户,剃须刀引擎甚至可以为UsePhysicalViewsIfNewer 获取参数。在这种情况下,它使用预编译版本,除非文件夹中放置了比已编译 .dll 更新的日期的视图。

这种方法应该可以解决视图的性能问题。

【讨论】:

  • 我可以详细说明这些内容,但可能需要先了解更多信息。
  • 感谢您的回复!我会在早上开会后测试一下!
  • @LarsHoldgaard 此更新可能会更永久地解决您的头痛问题,它已经解决了我之前的痛苦。您可以查看RazorGenerator 了解更多信息
【解决方案2】:

您是在捆绑设置中编译 Less 还是 Sass?
如果是,您使用的是什么 JavaScriptEngineSwitcher?
我记得有一个类似的问题。捆绑包是在首次访问时编译的,这需要花费大量时间。
解决方案是切换到 V8 引擎。

【讨论】:

  • 不确定。 Gruntfile 根据我的 SASS 文件编译 CSS,并且捆绑包仅包含 CSS 结果。我怀疑它是,但当然.. 我的 grunt 文件理论上可以做一些我没有想到的事情:prntscr.com/jf6d5c
  • 对我来说,包含在包中并在首次访问时编译的文件较少。对你来说似乎不是这样。
【解决方案3】:

我发布了一个答案,即使它没有解决,但我 99% 确定我已经找到了根本问题。

发生的情况是,当我发布时,每个视图都需要构建。构建一个视图大约需要 15 秒,这也是我在最新更新中 New Relic 所展示的。

这带来了两个临时解决方案和一个更大的问题:为什么视图的构建如此缓慢?

临时解决方案很简单。要么在发布时编译视图,要么在发布后访问最重要的页面。这显然很烦人,因为我每天发布多次。

我认为它这么慢的原因是因为我使用了一个非常大的 Bootstrap 主题。我处理捆绑包的方式不是很有效,这可能会带来问题。

我认为痛苦的原因是大约 10 分钟后网站变慢了,这仅仅是因为我经常发布新代码,并且没有访问我们的大部分页面。这样做之后,很快。

非常感谢您的帮助和支持——至少现在我可以应付了。

【讨论】:

  • Lars,您是否考虑过在您的 web.config 中指定要预热的页面,然后将您的部署与 CI/CD 设置中的插槽交换结合起来?我认为这可以解决你的问题。
【解决方案4】:

还有两个选项可以查找和修复。

1. 用户New Relic 进行跟踪。

查看来自 hanselman 的 this 帖子,了解如何在 Azure 中使用它。

2.遵循 Asp.Net MVC 最佳实践

查看this 帖子了解最佳做法。

对于数据读取,您可以将ADO.NET 与存储过程一起使用,而不是Entity Framework,因为到目前为止EF 可能存在一些性能问题。

【讨论】:

  • 实体框架本身没有性能问题。那里有很多误导性的帖子和不良做法。只要您知道使用 .AsNoTracking() 之类的事情,您就很好。存在更快的替代方案,但功能不丰富。 EF 还可以执行存储过程或原始 SQL。
【解决方案5】:
  1. 从部署和本地中分离出任何内容。如果应用程序在本地环境中运行良好,那么当它进入 Azure 时会发生一些不同的事情。解决问题需要很多时间。
  2. 任何静态资源(脚本、样式等)都会在第一次请求时被浏览器自动缓存,因此在后续请求中,问题应该不会出现。
  3. 由于您知道“渲染”方法会导致问题,因此看起来复杂的嵌套计算和浏览器 DOM 操作正在发生。但是,如果这在本地不是一个挑战,那么检查是否渲染外部资源的调用以及它们是否获得阻塞调用(可能需要大量使用 ajax 或服务器端异步调用,尽管我假设你已经有了它。)
  4. 可能需要进行重构(以拥有更小的堆栈并可能使用线程/非阻塞 IO),但需要查看您的代码以提出建议。

分享您的页面加载事件以及它进行的任何后续调用。以及视图是如何与任何 DOM 处理一起呈现的。

【讨论】:

  • 感谢您的回复!我会在早上开会后测试一下!
  • 那些缓慢的渲染步骤是服务器端的,而不是浏览器中基于 DOM 的渲染
  • 将服务器端代码添加到帖子中。还有应用程序在本地实例而不是 azure 中运行时的行为方式。本地网络上的资源应该比云端更快地可用
  • 我明天会更新这个。周末没时间看这个。感谢大家的尝试!
  • 当您完全在内部而不是 azure infra... 或者在您自己的机器上托管时会发生什么情况?它仍然行为不端吗?
【解决方案6】:

一些想法:

  1. 在 Web 应用边栏选项卡上,转至诊断和解决问题菜单。然后单击性能计数器。老实说,我会仔细检查每一个可用的性能计数器,注意时间线与性能下降的关系。我曾经通过查看线程数发现 SignalR 由于连接失控而阻塞了我的服务器。

  2. Application Insights 中的服务器错误日志是否干净?

  3. 在诊断和解决问题屏幕下,您是否在失败的请求跟踪日志中看到任何可疑内容?

【讨论】:

  • 嗨,罗伯!非常感谢您花时间提出一些想法! 1)一切看起来都非常干净。如果您看到我更新的答案,您可以看到我找到了缓慢请求的“证据”。在诊断中,我看不到挑战:prntscr.com/jcs28v。 &prntscr.com/jcs2hfprntscr.com/jcs2k7。我正在等待收集其余的 2) 是的,它很干净:prntscr.com/jcs1zw 3) 没有,很遗憾。似乎是空的 - prntscr.com/jcs4vs
  • 我们所说的解决方案是只包含 Web App 和 SQL DB,没有其他依赖项吗?
  • 是的。还有其他依赖项(一些微服务和一个 Azure 存储帐户),但在显示加载缓慢的页面上没有,所以我有点不确定它们会如何影响这一点
猜你喜欢
  • 2019-11-23
  • 2016-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-06
  • 2016-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多