我的node.js服务器遇到缓慢或无响应的时间,甚至偶尔会在尝试连接到服务器时导致503网关超时.
我99%肯定(基于我已经运行的测试),这种滞后特别来自我使用node-oauth模块联系外部API(Facebook,Twitter和许多其他人)的大量出站请求.无可否认,正在进行的出站请求数量相对较大(每分钟大约30个左右).更糟糕的是,这经常意味着对我的服务器的相应入站请求可能需要大约5-10秒才能完成.但是,我有一个以前用于PHP编写的API版本,它能够处理这一数量的出站请求而没有任何问题.实际上,使用我的Node.js API的相同数量(甚至更少)请求的CPU使用率大约是我的PHP API的5倍.
所以,我试图找出可以改进的地方,最重要的是要确保不会发生503超时.以下是我读过或试验过的一些内容:
我可以继续,但简而言之,我已经能够找到关于如何优化性能的确切信息,因此这些出站连接不会滞后于来自客户端的入站请求.
提前感谢任何想法或贡献.
FWIW,我也使用express和mongoose,我的服务器托管在Amazon Cloud上(2x M1.Large用于节点服务器,2x负载均衡器和3x M1.Small MongoDB实例).
我在我的mongostat输出中看到了一个巨大的(~200 ++)故障/秒数,但锁定率非常低:

我的Mongo服务器运行在亚马逊云上的m1.large实例上,因此它们每个都有7.5GB的RAM ::
root:~# free -tm
total used free shared buffers cached
Mem: 7700 7654 45 0 0 6848
Run Code Online (Sandbox Code Playgroud)
显然,我没有足够的内存来满足mongo想要做的所有事情(由于磁盘IO,导致巨大的CPU使用率%).
我发现这个文档表明在我的场景中(高故障,低锁%),我需要"扩展读取"和"更多磁盘IOPS".
我正在寻找有关如何最好地实现这一目标的建议.也就是说,我的node.js应用程序执行了很多不同的潜在查询,我不确定瓶颈在哪里发生.当然,我试过了
db.setProfilingLevel(1);
Run Code Online (Sandbox Code Playgroud)
但是,这对我没那么大帮助,因为输出的统计信息只是向我显示了慢查询,但是我很难将查询导致页面错误的信息翻译成...
正如您所看到的,这导致我的PRIMARY mongo服务器上的CPU等待时间很长(接近100%),尽管2x SECONDARY服务器不受影响......

以下是Mongo文档对页面错误的看法:
页面错误表示MongoDB要求数据不在物理内存中的次数,并且必须从虚拟内存中读取.要检查页面错误,请参阅serverStatus命令中的extra_info.page_faults值.此数据仅适用于Linux系统.
单独的页面错误很小并且很快完成; 但是,总的来说,大量的页面错误通常表明MongoDB正在从磁盘读取太多数据,并且可以指示许多潜在的原因和建议.在许多情况下,MongoDB的读锁定将在页面错误后"产生",以允许其他进程在等待下一页读入内存时读取并避免阻塞.这种方法可以提高并发性,在大容量系统中,这也可以提高整体吞吐量.
如果可能,增加MongoDB可访问的RAM量可能有助于减少页面错误的数量.如果无法做到这一点,您可能需要考虑部署分片群集和/或向部署添加一个或多个分片以在mongod实例之间分配负载.
所以,我尝试了推荐的命令,这非常无益:
PRIMARY> db.serverStatus().extra_info
{
"note" : "fields vary by platform",
"heap_usage_bytes" : 36265008,
"page_faults" : 4536924
}
Run Code Online (Sandbox Code Playgroud)
当然,我可以增加服务器大小(更多的RAM),但这是昂贵的,似乎有点矫枉过正.我应该实现分片,但实际上我不确定哪些集合需要分片!因此,我需要一种方法来隔离故障发生的位置(哪些特定命令导致故障).
谢谢您的帮助.
编辑
经过大量的讨论,我终于找到了一些似乎可能是一个坚实的领导:
快递库当前正在使用Node + OAuth模块执行多个出站请求(例如,Facebook,Twitter等)时,无法接受传入请求.我能够通过在我的代码中放置大量日志来确定这一点,在那里我发现在出站请求中间没有触发"开始请求"日志(如下所述).
我已经能够证明,当Node + OAuth模块发出一些出站请求时,对我的API的入站请求(通过浏览器窗口)将挂起,直到其中一个出站OAuth请求完成后才会收到.
当然,我已经完成了:
require('http').globalAgent.maxSockets = 999;
Run Code Online (Sandbox Code Playgroud)
根据IRC的建议,我补充道
console.log(require('http').globalAgent.requests);
Run Code Online (Sandbox Code Playgroud)
但这似乎总是=== {},暗示没有待处理的入站请求AFAIK.
因此,我得出结论,由于某些原因,node.js或express选择阻止由于出站请求而传入的请求,即使应该有足够的套接字可用...
任何人都有任何关于如何解决这个问题的提示?
我在node.js中使用部署在Amazon Cloud上的Express,Mongoose等创建了一个API,它在99%的时间内运行得非常快速.
除了,偶尔,请求似乎以某种方式被丢弃或以其他方式被忽略.我说的是通常在几毫秒内完成的请求随机无响应而没有明确的原因.
连接到API端点时,症状是一个简单的"网关超时".在同一个客户端使用相同的参数进行相同的请求,只需要在之前或之后,就可以正常工作.
当然,我的第一个想法是"呃,服务器超载!" 所以我花了很多时间来优化我的请求,monogoDB等.最后我得到了全面的CPU /磁盘/ RAM使用率(在Node.js服务器和Mongo服务器中)非常低.我使用Scout和RightScale实时跟踪我的服务器,并记录任何超过100毫秒的请求或查询.我的节点服务器目前有5GB的免费内存,70%的免费CPU(在第一个内核上),等等.所以我99.99%肯定它不是性能问题.
最后,我再次陷入绝望的尝试:我随机编号附加了我的客户提出的所有请求.然后,在node.js应用程序中,我在第一次收到请求时以及完成请求时执行console.log().例如,这是我在express中使用的中间件:
var configureAPI = function() {
return function(req, res, next) {
if(req.body.ruid)
console.log(req.body.ruid);
// more middleware stuff...
};
}
server.configure(function(){
server.use(express.bodyParser());
server.use(configureAPI());
server.use(onError);
// ... more config stuff
}
Run Code Online (Sandbox Code Playgroud)
我发现震惊了我:显然,node.js应用程序甚至没有收到有问题的请求.我有一个Javascript webapp,我将随请求发送的"ruid"打印到控制台.只要请求成功,node.js控制台中就会打印出相应的"ruid".每当它超时,就没有.
编辑:更多调试和信息.
我的应用服务器实际上已启动(并继续)也为PHP提供服务(因此,他们安装了Apache等).我需要http://streamified.me来服务我的网站(PHP)和http://api.streamified.me来提供我的API(node.js)...所以我的httpd.conf文件中有一行代码导致请求api.streamified.me(而不是streamified.me)通过端口8888转到node.js:
RewriteCond %{HTTP_HOST} ^api.streamified.me
RewriteRule ^(.*) http://localhost:8888$1 [P]
Run Code Online (Sandbox Code Playgroud)
因此,在同一个httpd.conf文件中,我启用了RewriteLogLevel 5,然后在我的localhost上创建了一个简单的PHP + CURL脚本,用随机URL命中我的api.streamified.me(这会导致node.js触发一个简单的"未找到"响应)直到导致网关超时.在这里,您可以看到它已经发生 …
我注意到我的node.js应用程序的RSS(Resident Set Size)随着时间的推移而增长,并且考虑到我的服务器上有"JS对象分配失败 - 内存不足"错误,这似乎是一个可能的原因.
我设置了以下非常简单的Node应用程序:
var express = require('express');
var app = express();
app.get('/',function(req,res,next){
res.end(JSON.stringify(process.memoryUsage()));
});
app.listen(8888);
Run Code Online (Sandbox Code Playgroud)
通过简单地按住"刷新"热键@ http:// localhost:8888 /我可以观看RSS /堆/等.增长,直到RSS远远高于50mb(在我感到无聊之前).等待几分钟然后回来,RSS下降 - 可能是GC运行了.
我试图弄清楚这是否解释了为什么我的实际节点应用程序崩溃...我的生产应用程序快速达到大约100Mb的RSS大小,当它崩溃时它通常在200Mb-300Mb之间.尽我所知,这不应该太大(节点应该能够处理1.7Gb左右,我相信)但是我仍然担心我的生产服务器上的RSS大小趋势向上(衰落)代表崩溃):

自动加载(和重新加载)通常在我的Ruby(2.3.0)on Rails(5.0.1)项目中正常工作.但是,在开发模式下,我偶尔会看到如下错误:
无法自动加载常量Foo :: Bar,预期/app/models/foo/bar.rb来定义它
这是意外的,因为:
foo/bar.rb事实上,file()确实定义了Foo::Bar.而且,代码foo/bar.rb很简单:
module Foo
class Bar < CustomRecord
end
end
Run Code Online (Sandbox Code Playgroud)
简单的解决方法是重新启动服务器,然后再次发送请求(总是成功).FWIW,我正在使用zeus server.
我最好的猜测是没有重新加载某些东西,但我不确定如何进行调试.我似乎无法指出导致问题的任何具体行动.有时编辑代码会导致它发生,有时不会.
我的浏览器扩展程序偶尔会崩溃.问题是,我找不到一个可以导致扩展崩溃的好的,全面的事物列表,因此很难创建一个可以使用的事项清单.
我的假设是,导致标准Chrome标签崩溃的任何内容都会导致扩展程序在Background.html文件中运行时崩溃.
在我的头顶,我假设以下可能会导致问题......
这就是我能想到的全部.我有一段时间试图调试我的扩展程序,非常感谢任何创建清单的帮助......
背景:我正在尝试优化我在Amazon Cloud上使用Express和Mongoose构建的Node.js API的速度.我有一个API调用需要花费大量的时间来运行(我的/ stats API调用编译来自许多源的数据,因此产生了数百个mongo查询,因此运行大约需要20秒).我注意到,当这个API调用正在运行时,其他也调用了Mongo副本集的API调用返回的速度很慢.我的第一个想法是统计查询缓慢,因此阻塞,但根据我的统计小组,我没有任何查询需要超过100毫秒运行,而且我的Mongo DB统计数据都在相当健康的范围内(每个20多个查询)第二,<1%btree未命中,<5%锁定).
问题:现在,我的Node.js应用程序在启动时建立了与Mongo集的单个连接,并且所有查询都使用该连接.建立多个连接会更好吗?例如,我应该为每个入站API请求建立一个新的Mongo连接吗?或者,也许我应该有一个静态数量的连接到副本集,并在执行查询时这些连接之间的负载平衡?
或者我可能完全偏离基地?
我想为我的node.js应用生成火焰图.不幸的是,我的dev的盒子是OSX(不支持utrace帮手,每个链接的文章),我的生产箱是CentOS的(甚至没有的DTrace).
我发现有些迹象表明像SystemTap这样的东西可能是一种替代方案,但我一直无法拼凑出一种有效的工作方式来生成适当的stacks.out文件以提供给stackvis.
有没有人知道一个关于如何启动和运行的体面教程?我宁愿它在CentOS(这样我就可以检查我的生产应用),但OSX也将是足够的.
我使用请求模块以非常标准的方式连接到我的node.js应用程序中的其他网站:
Network.prototype.httpRequest = function(url, method, params, headers, callback)
{
var qs = typeof params === 'string' ? params : querystring.stringify(params || {}),
config = {
'method': (method || 'get').toUpperCase(),
'url': url,
'timeout': this.app.api.settings.http.timeout_outbound
};
if(config.method === 'GET')
{
config.url += '?' + qs;
}
else
{
config.body = qs;
}
request(config, callback);
};
Run Code Online (Sandbox Code Playgroud)
在我的应用程序运行几个小时后,此功能开始超时 - 而不仅仅是一个网站.它开始无法连接(ETIMEDOUT)随机,没有任何可辨别的模式.
我的第一个明显的猜测是必须以某种方式阻止/干扰网络连接.在我的应用程序中有很多其他"移动部件"(例如,通过Mongoose连接到Mongo,通过Elastical连接到ElasticSearch等),因此可能可以想象其中一个是原因...但显然,它是不是真的有选择地选择性地禁用其他模块并等待6个小时以查看问题是否消失......
我正在监控网络流量(通过OSX上的iStatMenus pro,FWIW),并且看不到来自节点的任何异常/持续流量.
有没有其他方法可以深入研究可能阻塞/干扰我的流量的内容?
我开始使用 Prettier + eslint 使用pretty-quick& husky(选项 2)。它的行为符合预期,即在提交(或修改)期间重新格式化代码并包含提交中的更改。
我的 monorepo 需要(几个)更多的预提交钩子,所以我最终迁移到pre-commit.com(选项 3)。现在,当我提交或修改时,Prettier 会修改文件并返回Failed状态。这造成了一个相当烦人的工作流程,我被迫添加文件并尝试再次提交更改。
有什么方法可以将更改重新暂存为提交的一部分吗?