当前位置: 首页 > 后端技术 > Node.js

Node.js指南(轻松分析Node.js应用程序)

时间:2023-04-03 12:06:42 Node.js

轻松分析Node.js应用程序有许多第三方工具可用于分析Node.js应用程序,但在许多情况下,最简单的选择是使用Node。js.js内置分析器,内置分析器利用V8内部的分析器在程序执行过程中周期性地对栈进行采样,并将这些采样的结果以及重要的优化事件(如jit编译)记录为一系列ticks:code-creation,LazyCompile,0,0x2d5000a337a0,396,"bpnativearray.js:1153:16",0x289f644df68,~code-creation,LazyCompile,0,0x2d5000a33940,716,"h??asOwnPropertynativev8natives.js:198:30",0x289f64438d0,~code-creation,LazyCompile,0,0x2d5000a33c20,284,"ToNamenativeruntime.js:549:16",0x289f643bb28,~code-creation,Stub,2,0x2d5000a33d40,182,"DoubleToIStub"代码-creation,Stub,2,0x2d5000a33e00,507,"NumberToStringStub"过去,您需要V8源代码来解释tick,幸运的是,Node.js4.4中引入了一些工具。仅从源代码构建V8,让我们看看内置的分析器如何帮助深入了解应用程序性能。为了说明tick分析器的使用,我们将使用一个简单的Express应用程序,我们的应用程序将有两个处理程序,一个用于向我们的系统添加新用户:app.get('/newUser',(req,res)=>{letusername=req.query.username||'';constpassword=req.query.password||'';username=username.replace(/[!@#$%^&*]/g,'');if(!username||!password||users.username){returnres.sendStatus(400);}constsalt=crypto.randomBytes(128).toString('base64');consthash=crypto.pbkdf2Sync(密码,salt,10000,512,'sha512');users[用户名]={salt,hash};res.sendStatus(200);});另一个用于验证用户身份验证尝试:app.get('/auth',(req,res)=>{letusername=req.query.username||'';constpassword=req.query.password||'';username=username.replace(/[!@#$%^&*]/g,'');if(!username||!password||!users[username]){returnres.sendStatus(400);}const{salt,hash}=users[用户名];constencryptHash=crypto.pbkdf2Sync(password,salt,10000,512,'sha512');if(crypto.timingSafeEqual(hash,encryptHash)){res.sendStatus(200);}else{res.sendStatus(401);}});请注意,这些不是推荐用于在Node.js应用程序中对用户进行身份验证的处理程序,仅用于说明目的,您通常不应尝试设计自己的加密身份验证机制,最好使用现有的、经过验证的身份验证解决方案现在假设我们已经部署了我们的应用程序并且用户抱怨请求的高延迟,我们可以使用内置的分析器轻松运行应用程序:NODE_ENV=productionnode--profapp.js并在服务器上使用ab(ApacheBench)添加一些负载:curl-XGET"http://localhost:8080/newUser?username=matt&password=password"ab-k-c20-n250"http://localhost:8080/auth?username=matt&password=password"并获得ab输出:并发级别:20测试时间:46.932秒完成请求:250失败请求:0保持活动请求:250总传输:50250字节HTML传输:500字节每秒请求数:5.33[#/sec](:3754.556[ms](平均值)每个请求的时间:187.728[ms](平均值,跨所有并发请求)传输速率:1.05[Kbytes/sec]收到...在特定时间内服务的请求百分比(毫秒)50%375566%380475%381880%382590%384595%385898%387499%3875100%4225(longestrequest)从这个输出我们可以看到我们每秒只能处理大约5个请求,平均请求往返时间少于4秒。在实际示例中,我们可以代表用户请求在许多函数中做很多工作,但即使在我们的简单示例中,编译正则表达式、生成随机盐、从用户密码生成唯一哈希,或者在Expressframework在内部,时间可能会丢失。由于我们使用--prof选项运行我们的应用程序,因此在与本地应用程序运行相同的目录中生成了一个tick文件,它的格式应该是isolate-0xnnnnnnnnnnnn-v8.log(其中n是一个数字)。为了理解这个文件,我们需要使用与Node.js二进制文件捆绑在一起的tick处理器,使用--prof-process标志运行处理器:node--prof-processisolate-0xnnnnnnnnnnnn-v8.log>processed。txt在你最喜欢的文本编辑器中打开processed.txt会给你一些不同类型的信息,文件被分成几个部分,然后再按语言细分,首先我们看摘要部分,然后看:[摘要]:tickstotalnonlibname790.2%0.2%JavaScript3670397.2%99.2%C++70.0%0.0%GC7672.0%Sharedlibraries2150.6%Unaccounted这告诉我们收集的所有样本中有97%出现在C++代码中,当查看时在处理输出的其他部分,我们应该最关心用C++(而不是JavaScript)完成的工作,考虑到这一点,我们接下来找到[C++]部分,其中包含有关哪些C++函数占用最多CPU的信息时间信息,并查看:[C++]:tickstotalnonlibname1955751.8%52.9%node::crypto::PBKDF2(v8::FunctionCallbackInfoconst&)451011.9%12.2%_sha1_block_data_order31658.4%8.6%_malloc_zone_malloc我们看到前三个条目占程序使用的CPU时间的72.1%。从这个输出中,我们立即看到至少51.8%的CPU时间被一个名为PBKDF2的函数占用,该函数对应于对密码进行哈希处理。然而,较低的两个条目如何影响我们的应用程序可能不是很明显(或者如果我们为了示例假装不是这样),为了更好地理解这些功能之间的关系,接下来我们将看看[自下而上(重)profile]部分,它提供了每个函数的主要调用者的信息,检查这个部分,我们发现:ticksparentname1955751.8%node::crypto::PBKDF2(v8::FunctionCallbackInfoconst&)19557100.0%v8::internal::Builtins::~Builtins()19557100.0%LazyCompile:~pbkdf2crypto.js:557:16451011.9%_sha1_block_data_order4510100.0%LazyCompilejs:*pbk:557:164510100.0%LazyCompile:*exportsLazyCompile:*出口.pbkdf2Synccrypto.js:552:3031658.4%_malloc_zone_malloc316199.9%LazyCompile:*pbkdf2crypto.js:557:163161100.0%LazyCompile:*exports.pbkdf2Synccrypto.js:552:30解析这部分需要更多工作比上面的原始滴答计数,并且在上面的每个“调用堆栈”中,父列中的百分比告诉您当前行中的哪个样本函数调用了百分比上方行中的函数。例如,在sha1block_data_order上方的中间“调用堆栈”中,我们看到_sha1_block_data_order出现在11.9%的样本中,这是我们从上面的原始计数中得知的。然而,在这里我们也可以告诉它总是由Node.js加密模块中的pbkdf2函数调用,我们看到类似地,_malloc_zone_malloc几乎完全由同一个pbkdf2函数调用。因此,使用此视图中的信息,我们可以看出用户密码帐户不仅针对上述51.8%进行了哈希处理,而且针对前3个最采样函数中的所有CPU时间进行了哈希处理,因为_sha1_block_data_order和_malloc_zone_malloc调用是在代表pbkdf2函数。在这一点上,很明显基于密码的哈希生成应该是我们的优化目标。值得庆幸的是,您已经完全内化了异步编程的好处,并且您意识到从用户密码生成哈希是同步进行的。完成,从而绑定事件循环,这会阻止我们在计算哈希时处理其他传入请求。要解决此问题,请对上述处理程序进行小幅修改以使用pbkdf2函数的异步版本:app.get('/auth',(req,res)=>{letusername=req.query.username||'';constpassword=req.query.password||'';username=username.replace(/[!@#$%^&*]/g,'');if(!username||!password||!users[username]){returnres.sendStatus(400);}crypto.pbkdf2(password,users[username].salt,10000,512,(err,hash)=>{if(users[username].hash.toString()===hash.toString()){res.sendStatus(200);}else{res.sendStatus(401);}});});以上ab基准测试针对您的应用程序的异步版本新的运行结果:并发级别:20测试时间:12.846秒完成请求:250失败请求:0保持活动请求:250总传输:50250字节HTML传输:500字节每秒请求数:19.46[#/sec](:1027.689[ms](平均值)每个请求的时间:51.384[ms](平均值,跨所有并发请求)传输速率:3.82[Kbytes/sec]received...一定时间内服务的请求百分比(ms)50%101866%103575%104180%104390%104995%106398%107099%1071100%1079(最长请求)优秀了!您的应用程序现在每秒处理大约20个请求,大约是同步哈希生成的4倍,而且平均延迟从之前的4秒下降到1秒多一点。希望通过这个(公认设计的)示例的性能调查,您已经看到V8tick处理器如何帮助您更好地了解Node.js应用程序的性能。Previous:入门指南Next:DockerizingNode.jsWeb应用程序