Azure Web 应用上节点应用程序的最佳做法和故障排除指南

本文介绍 Azure Web 应用上运行的 node 应用程序的最佳实践和故障排除步骤(使用 iisnode)。

Warning

在生产站点上使用故障排除步骤时,请格外小心。 建议在非生产安装(例如过渡槽)上排查应用问题,问题修复后,请交换过渡槽与生产槽。

IISNODE 配置

架构文件显示可针对 iisnode 配置的所有设置。 适用于应用程序的一些设置如下:

  • nodeProcessCountPerApplication

    此设置控制每个 IIS 应用程序启动的节点进程数目。 默认值为 1。 可将此值设置为 0,启动与 VM vCPU 计数一样多的 node.exe。 大多数应用程序的建议值为 0,以便利用计算机上的所有 vCPU。 Node.exe 占用单线程,因此,一个 node.exe 最多消耗用 1 个 vCPU。 要让 node 应用程序发挥最大性能,可以利用所有 vCPU。

  • nodeProcessCommandLine

    此设置控制 node.exe 的路径。 可以设置此值以指向 node.exe 版本。

  • maxConcurrentRequestsPerProcess

    此设置控制 iisnode 发送给每个 node.exe 的并发请求数目上限。 在 Azure Web 应用中,此设置的默认值为“无限”。 不必担心此设置。 在 Azure Web 应用外部,默认值为 1024。 可以根据应用程序收到的请求数目以及应用程序处理每个请求的速度来配置此设置。

  • maxNamedPipeConnectionRetry

    此设置控制 iisnode 会在命名管道上重试连接,以将请求发送至 node.exe 的次数上限。 此设置与 namedPipeConnectionRetryDelay 结合使用,可确定 iisnode 内每个请求的总超时。 在 Azure Web 应用中,默认值为 200。 总超时(秒)= (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000

  • namedPipeConnectionRetryDelay

    此设置控制 iisnode 在每次重试通过命名管道将请求发送到 node.exe 之间所要等待的时间(毫秒)。 默认值为 250 毫秒。 总超时(秒)= (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000

    在 Azure Web 应用中,iisnode 的总超时默认为 200 * 250 毫秒 = 50 秒。

  • logDirectory

    此设置控制 iisnode 用于记录 stdout/stderr 的目录。 默认值是相对于主要脚本目录(主要 server.js 所在的目录)的 iisnode

  • debuggerExtensionDll

    此设置控制 iisnode 在调试 node 应用程序时使用的节点检查器版本。 iisnode-inspector-0.7.3.dll 和 iisnode-inspector.dll 目前是此设置仅有的两个有效值。 默认值为 iisnode-inspector-0.7.3.dll。 iisnode-inspector-0.7.3.dll 版本使用 node-inspector-0.7.3 并使用 WebSocket。 必须在 Azure Web 应用中启用 WebSocket 才能使用此版本。 若要详细了解如何将 iisnode 配置为使用新节点检查器,请参阅 http://www.ranjithr.com/?p=98

  • flushResponse

    IIS 的默认行为是在刷新之前或直到响应结束时(以较早出现者为准),缓冲最多 4MB 的响应数据。 iisnode 会提供配置设置来替代此行为:若要在 iisnode 从 node.exe 收到响应实体主体片段时立刻刷新该片段,需在 web.config 中将 iisnode/@flushResponse 属性设置为“true”:

    <configuration>    
        <system.webServer>    
            <!-- ... -->    
            <iisnode flushResponse="true" />    
        </system.webServer>    
    </configuration>
    

    支持刷新每个响应实体主体片段会增加性能开销,而降低约 5% 的系统吞吐量(从 v0.1.13 起),因此,最好让此设置的范围仅限于需要响应流式处理的终结点(例如,在 web.config 中使用 元素)。

    除此之外,对于流式处理应用程序,必须将 iisnode 处理程序的 responseBufferLimit 设置为 0。

    <handlers>    
        <add name="iisnode" path="app.js" verb="\*" modules="iisnode" responseBufferLimit="0"/>    
    </handlers>
    
  • watchedFiles

    这是一个以分号分隔的文件列表,系统将监视其更改。 文件更改会导致应用程序回收。 每个条目都包含可选目录名称,再加上相对于主要应用程序入口点所在目录的必要文件名。 只有文件名部分可以使用通配符。 默认值为“*.js;web.config”

  • recycleSignalEnabled

    默认值为 false。 如果启用,节点应用程序可以连接到命名管道(环境变量 IISNODE_CONTROL_PIPE)并发送“回收”消息。 可以通过此方式正常回收 w3wp。

  • idlePageOutTimePeriod

    默认值为 0,表示已禁用此功能。 如果设置为大于 0 的值,iisnode 会每隔“idlePageOutTimePeriod”毫秒将其所有子进程移出分页。 若要了解移出分页的含义,请参考文档。 对于消耗大量内存并且偶尔想要将内存页出至磁盘以释放一些 RAM 的应用程序,此设置很有用。

Warning

在生产应用程序上启用以下配置设置时,请格外小心。 建议不要在实际生产应用程序上启用它们。

  • debugHeaderEnabled

    默认值为 false。 如果设置为 true,iisnode 会将 HTTP 响应标头 iisnode-debug 添加到它所发送的每个 HTTP 响应,而 iisnode-debug 标头值是 URL。 查看 URL 片段即可收集个别诊断信息,但在浏览器中打开 URL 可达到更好的视觉效果。

  • loggingEnabled

    此设置控制 iisnode 记录 stdout 和 stderr 的功能。 Iisnode 从它所启动的节点进程捕获 stdout/stderr,并写入到“logDirectory”设置中指定的目录。 一旦启用,应用程序会将日志写入文件系统,这可能会影响性能,但具体要视应用程序完成的日志记录量而定。

  • devErrorsEnabled

    默认值为 false。 如果设置为 true,iisnode 会在浏览器上显示 HTTP 状态代码和 Win32 错误代码。 在调试特定类型的问题时,win32 代码很有用。

  • debuggingEnabled(请勿在实际生产站点上启用)

    此设置控制调试功能。 Iisnode 与节点检查器集成。 通过启用此设置,可启用节点应用程序的调试功能。 启用此设置后,iisnode 会在对 node 应用程序发出第一个调试请求时,在“debuggerVirtualDir”目录中布置所需的节点检查器文件。 可以将请求发送到 http://yoursite/server.js/debug,加载节点检查器。 可以使用“debuggerPathSegment”设置来控制调试 URL 段。 默认情况下,debuggerPathSegment='debug'。 可将其设置为 GUID 之类的值,这样,其他人就更难发现。

    访问此链接,更详细地了解调试。

方案和建议/故障排除

节点应用程序发出的出站调用太多。

许多应用程序想要在其定期操作中进行出站连接。 例如,请求传入时,节点应用程序会想连接别处的 REST API,并获取一些信息来处理请求。 建议在进行 http 或 https 调用时使用保持连接代理。 例如,可以在进行这些出站调用时,使用 agentkeepalive 模块作为保持连接代理。 这可确保在 Azure Web 应用 VM 上重复使用套接字,从而减少为每个出站请求创建新套接字的开销。 此外,还可确保使用较少的套接字来发出许多出站请求,以便不超出每个 VM 分配的 maxSockets。 对于 Azure Web 应用的建议是将 agentKeepAlive maxSockets 值设置为每个 VM 总共 160 个套接字。 这意味着,如果有四个 node.exe 在 VM 上运行,需要将每个 node.exe 的 agentKeepAlive maxSockets 设置为 40,也就是每个 VM 总共 160 个。

agentKeepALive 配置示例:

var keepaliveAgent = new Agent({    
    maxSockets: 40,    
    maxFreeSockets: 10,    
    timeout: 60000,    
    keepAliveTimeout: 300000    
});

此示例假设有 4 个 node.exe 在 VM 上运行。 如果有不同数目的 node.exe 在 VM 上运行,则必须相应地修改 maxSockets 设置。

节点应用程序耗用太多 CPU。

可能会在门户上获取 Azure Webapps 针对高 CPU 耗用量提供的建议。 也可以设置监视器,监视某些指标。 在 Azure 门户仪表板上检查 CPU 使用率时,请检查 CPU 的 MAX 值,这样才不会错过峰值。 如果觉得应用程序消耗了过多的 CPU,但又无法做出解释,可以分析 node 应用程序来找出原因。

在 Azure Web 应用中使用 V8 探查器分析 node 应用程序

例如,假设需要分析如下所示的 hello world 应用:

var http = require('http');    
function WriteConsoleLog() {    
    for(var i=0;i<99999;++i) {    
        console.log('hello world');    
    }    
}

function HandleRequest() {    
    WriteConsoleLog();    
}

http.createServer(function (req, res) {    
    res.writeHead(200, {'Content-Type': 'text/html'});    
    HandleRequest();    
    res.end('Hello world!');    
}).listen(process.env.PORT);

转到 scm 站点 https://yoursite.scm.chinacloudsites.cn/DebugConsole

此时会出现如下所示的命令提示符。 进入 site/wwwroot 目录。

运行“npm install v8-profiler”命令。

这应该会在 node_modules 目录及其所有依赖项之下安装 v8 探查器。 现在,编辑 server.js 以分析应用程序。

var http = require('http');    
var profiler = require('v8-profiler');    
var fs = require('fs');

function WriteConsoleLog() {    
    for(var i=0;i<99999;++i) {    
        console.log('hello world');    
    }    
}

function HandleRequest() {    
    profiler.startProfiling('HandleRequest');    
    WriteConsoleLog();    
    fs.writeFileSync('profile.cpuprofile', JSON.stringify(profiler.stopProfiling('HandleRequest')));    
}

http.createServer(function (req, res) {    
    res.writeHead(200, {'Content-Type': 'text/html'});    
    HandleRequest();    
    res.end('Hello world!');    
}).listen(process.env.PORT);

上述代码将会分析 WriteConsoleLog 函数,然后将配置文件输出写入站点 wwwroot 下的“profile.cpuprofile”文件。 将请求发送到应用程序。 站点 wwwroot 下会显示所创建的“profile.cpuprofile”文件。

请下载此文件,并使用 Chrome F12 工具将其打开。 在 Chrome 中按 F12,并选择“配置文件”选项卡。单击“加载”按钮。 选择下载的 profile.cpuprofile 文件。 单击刚加载的配置文件。

可以看到,WriteConsoleLog 函数已耗用 95% 的时间。 这也会显示造成此问题的具体行号和源文件。

节点应用程序耗用太多内存。

如果应用程序消耗过多的内存,门户中会显示 Azure Web 应用针对高内存消耗量提供的建议。 可将监视器设置为监视某些指标。 在 Azure 门户仪表板上检查内存使用率时,请务必检查内存的最大值,这样才不会错过峰值。

node.js 的泄漏检测和堆区分

可以使用 node-memwatch 帮助识别内存泄漏。 可以像安装 v8 探查器一样安装 memwatch,并编辑代码来捕获和区分堆,找出应用程序中的内存泄漏。

node.exe 随机终止

发生这种情况有以下几个原因:

  1. 应用程序正在引发未捕获的异常 - 请检查 d:\home\LogFiles\Application\logging-errors.txt 文件,了解有关所引发异常的详细信息。 此文件有堆栈跟踪,因此可据此修复应用程序。
  2. 应用程序耗用太多内存,其他进程无法开始运行。 如果 VM 内存总量接近 100%,进程管理器会终止 node.exe,让其他进程有机会执行一些操作。 若要解决此问题,请确保应用程序未泄漏内存;如果应用程序需要使用大量的内存,请扩展为具有更多 RAM 的较大 VM。

节点应用程序未启动

如果应用程序在启动时返回 500 错误,可能有几个原因:

  1. Node.exe 未出现在正确的位置。 检查 nodeProcessCommandLine 设置。
  2. 主要脚本文件未出现在正确的位置。 检查 web.config,确保处理程序节中的主要脚本文件名与主要脚本文件匹配。
  3. Web.config 配置不正确 - 检查设置名称/值。
  4. 冷启动 - 应用程序的启动时间太长。 如果应用程序所花的时间超过 (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000 秒,则 iisnode 会返回 500 错误。 增加这些设置的值,使其与应用程序启动时间匹配,防止 iisnode 超时并返回 500 错误。

节点应用程序崩溃

应用程序正在引发未捕获的异常 - 请检查 d:\home\LogFiles\Application\logging-errors.txt 文件,了解有关所引发异常的详细信息。 此文件有堆栈跟踪,因此可据此修复应用程序。

node 应用程序的启动时间太长(冷启动)

应用程序启动时间过长的最常见原因是 node_modules 中有大量文件。 应用程序在启动时会尝试加载其中的大多数文件。 默认情况下,因为文件位于 Azure Web 应用的网络共享上,所以加载太多文件可能需要一段时间。 加速此过程的某些解决方法包括:

  1. 使用 npm3 来安装模块,确保采用平面依赖关系结构,并且没有重复的依赖项。
  2. 尝试延迟加载 node_modules,而不要在应用程序启动时加载所有模块。 这意味着,对 require('module') 的调用应在尝试使用模块的函数中有实际需要时进行。
  3. Azure Web 应用提供一项称为本地缓存的功能。 此功能会将内容从网络共享复制到 VM 上的本地磁盘。 由于文件位于本地,因此 node_modules 的加载速度要快很多。

IISNODE http 状态和子状态

源文件列出了 iisnode 可在发生错误时返回的所有可能的状态/子状态组合。

为应用程序启用 FREB 以查看 win32 错误代码(出于性能方面的原因,请确保只在非生产站点上启用 FREB)。

Http 状态 Http 子状态 可能的原因?
500 1000 将请求分派到 IISNODE 时发生某种问题 - 检查 node.exe 是否已启动。 Node.exe 可能在启动时已崩溃。 检查 web.config 配置是否有错误。
500 1001 - Win32Error 0x2 - 应用未响应 URL。 检查 URL 重写规则,或检查是否为 Express 应用定义了正确的路由。 - Win32Error 0x6d - 命名管道繁忙 - Node.exe 不接受请求,因为管道繁忙。 检查 CPU 使用率是否偏高。 - 其他错误 - 检查 node.exe 是否已崩溃。
500 1002 Node.exe 崩溃 - 检查 d:\home\LogFiles\logging-errors.txt 中的堆栈跟踪。
500 1003 管道配置问题 - 此问题应该永远不会出现,但如果确实出现,则表示命名管道配置不正确。
500 1004-1018 发送请求或处理 node.exe 的相关响应时发生某个错误。 检查 node.exe 是否已崩溃。 检查 d:\home\LogFiles\logging-errors.txt 中的堆栈跟踪。
503 1000 内存不足,无法分配更多命名管道连接。 检查应用为何耗用大量内存。 检查 maxConcurrentRequestsPerProcess 设置值。 如果此值并非无限,而且有许多请求,请增大此值来防止此错误。
503 1001 无法将请求分派至 node.exe,因为应用程序正在回收。 应用程序回收之后,应该会正常处理请求。
503 1002 检查 win32 错误代码的实际原因 - 无法将请求分派到 node.exe。
503 1003 指定的管道太忙 - 检查节点是否正在消耗过多的 CPU

NODE.exe 内有名为 NODE_PENDING_PIPE_INSTANCES 的设置。 默认情况下,此值在 Azure Web 应用外部为 4。 这表示 node.exe 在命名管道上一次只能接受四个请求。 在 Azure Web 应用中,此值设置为 5000。 此值应足以满足 Azure Web 应用中运行的大多数 node 应用程序。 Azure Web 应用中不应出现 503.1003,因为 NODE_PENDING_PIPE_INSTANCES 的值较高。 |

更多资源

请访问以下链接,详细了解 Azure App Service 上的 node.js 应用程序。