Node.js的CPU密集计算的终结解决方案!!!
发布于 6 年前 作者 zy445566 7644 次浏览 最后一次编辑是 4 年前 来自 分享

这篇文章是早期写的存在很多问题,请忽略

好吧我承认我是标题党,但这种方法确实解决了node的CPU密集型计算时造成服务无法响应或响应过慢问题,其实我讲的的是Faas的serverless简单使用,基于某云平台进行讲解。

写在前面:

大家都知道node.js基于EventLoop,本质上就是对异步进行排队运算,所以这就面临一个问题,如果大量计算放入了队列中运算,后面就要等前面运行完,严重导致队列堆积导致请求超时之类的问题。但目前很多人用线程来解决,我觉得这样不好,会增加代码复杂度,并且如果有海量运算的话也没办法开无数多的线程(爆机器问题(所以有线程池)和大于逻辑核的无意义)。那我们有没有不增加复杂度,来解决这个问题呢?的确有,Faas框架来实现的serverless就能将CPU密集计算问题迎刃而解(原因文末说明)。最早是亚马逊的lambda,当目前国内也有类似实现,那我就选择国内某云的函数计算来验证是否解决CPU密集计算问题,同时会对利用某云函数计算的使用进行一次详解(本来是写了具体云平台,但为了避免广告嫌疑改某云)。

首先我们要创建一个函数计算的服务和方法

当然我们要先选择某云的产品列表的“函数计算”来创建服务 createServer 一般选择白板函数就好 createFunc1 我们用著名的斐波那契来模拟CPU密集型吧 createFunc2 代码奉上

module.exports.handler = function(event, context, callback) { 
  let req = JSON.parse(event);
  //为什么是40,因为每个函数计算时间不能超过3秒
  let num = req.queryParameters.num>0?
      req.queryParameters.num:
  	  Math.ceil(Math.random()*40);
  //由于这个直接作为页面数据,所以api网关需要状态码和页面显示信息
  callback(null,{
    body:`num is ${num},fibo is ${fibo(num)}`,
    statusCode:200}
  ); 
};

function fibo(num)
{
	if(num<2){return 1;} 
  	return fibo(num-1)+fibo(num-2);
}

接下来创建对应的Api网关

由于函数计算的方法可以互相调用和触发调用,api网关的作用就是为了通过用户访问网关触发函数计算的方法调用

当然我们要先选择某云的产品列表的“api网关”来创建网关服务 创建分组后,走如下图步骤 安全认证的作用是只让自己的手机App或自己的SDK用启用的,我们希望每个用户都能访问,改成无认证就好 api1 接下来定义自己给别人访问的url,同时可以将自己域名绑定到自己的分组,实现通过自己域名访问 api2 这里的要点就是选择函数服务,然后填入函数计算当时创建的服务名和方法名就好。 api3 最后发布上线就好,当然你还可以在外面的api试调先调用试试。 api4

对比一下自己本机开的服务进行压测

本机代码

var http = require('http');
var url = require('url');

function fibo(num)
{
	if(num<2){return 1;} 
  	return fibo(num-1)+fibo(num-2);
}

http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    var params = url.parse(req.url, true).query;
    let num = params.num>0?params.num:Math.ceil(Math.random()*40);
    res.end(`num is ${num},fibo is ${fibo(num)}`);
 
}).listen(3000);

压测结果

某云函数计算压测结果(我认为出现计算超时丢包是正确的,但也印证该云平台没有根据我想要的及时伸缩): test1 本机压测结果(已被CPU密集计算阻塞): test2

原因

由于函数计算会在访问量大的时候进行动态伸缩(其实就是加实例和机器,但只对需要的函数伸缩不像Paas要对整个平台伸缩造成浪费)所以在CPU密集时,它会为了保持及时的响应速度,进行进行伸缩,不至于CPU密集会导致无法访问或访问过慢。

最后

如果使用此某云实现的Serverless,那么建议使用该云推出开发工具,因为能更快用自己的IDE进行编码和开发,本人使用web页面,纯粹是为了更好展现功能。原文地址:https://github.com/zy445566/myBlog/tree/master/20171214serverless

17 回复

好不容易发个科普性水贴啊啊啊😯

这种“曲线救国”的策略是没人用的。

有空去写这么一个云平台的人也是蛋疼。

有那个空去学习/使用那个云平台,但不如花点时间,多学习其他语言。

让nodejs做它擅长的事,不擅长的交给其他语言去做。

serverless拿来做CPU密集计算? 允许我懵逼一会…

node已经支持多线程了啊: napajs.

@axetroy 我的重点不在某个云平台上。 而是证明Faas架构下可以解决node的CPU密集运算的问题。而且我觉得Faas一定是未来的趋势。而真正可伸缩Faas开源框架的出现只是时间问题。 侧重点是在未来可能任何语言的性能问题都会被大量缩小,语言未来的重点肯定会集中在体验和开发速度方面。 当然我也可能是个傻逼,时间会证明一切。

@XiaozhongLiu 我上面说了,并不觉得多线程是个好的解决方案。 而且我觉得充分利用单机性能会被弱化, 因为我更倾向利用海量渣渣实例(比如一台中或小型级开海量渣渣实例)来支持性能。 虽然现在很多公司也是这么干的😂

这样通信的成本大于 cpu消耗的成本

@yakczh 反向代理转发到各个渣渣机上,没有你想的耗性能,API网关其实就是起一个代理的作用

来自酷炫的 CNodeMD

这个方法的前提依旧是完整的计算过程也并没有非常的耗时,如果真的是一个复杂的长时间的 cpu 密集型运算,分散到无数个实例不能缓解单个计算响应慢甚至由此导致的 OOM 的问题,而如果对计算本身进行拆分,又要自己做一套数据同步 所以归根结底,还是要去衡量合适的语言来去做合适的事

@hyj1991 这些计算的异构处理,faas平台已经帮你做掉了. 你唯一关心的就是,输入和输出.

其实按照楼主的观点,肯本没必要用faas这种目前还没有很好的案例和实施复杂的东西. 直接用RESTful把cpu密集计算切出去然后用java或者c实现下不就得了?

faas也不是用来干这个的. 而且如果这是高频接口,这样的收费可能反而更贵.

@178220709 我就是这个意思,rest 处理是一种方式,就算认死 node ,也可以尝试下写扩展来做

@hyj1991 理论上是计算本身拆分,拆分成小函数,只要调用这些小函数就能实现计算动态伸缩分摊。 只不过目前至少现在的云平台做的还不够好,就算真这样拆分理论上现有的云平台未必能够实现。

@zy445566 计算本身的拆分我觉得不大可能平台自动给你做掉,这属于业务逻辑部分还是得要你自己去控制的,这样做了其实和楼上提到的 rest 的方案复杂度没差多少,而且逻辑拆分过于零散了还不易于后期的维护升级

@178220709 对的,现有的Faas云服务现在其实还差的很远。 也没有很好的案例,同时实现技术上也是千差万别,没有成立协会统一标准。 但我觉得以后未必就不行

@hyj1991 嗯嗯,毕竟缺点是必然存在的,没有东西是十全十美的

。。。。。。。。。。。。。。我宁愿跨进程调用c程序都比这个看着方便。。。

@alsotang 哈哈,看起来是有点恶心😄😄😄 其实是有工具的,用工具会方便很多,我只是方便讲解所以用了页面。 @JacksonTian 应该很熟悉工具

我在去年的 沪JS 上演示过,基于 API Gateway 和 函数计算,搭建无服务器应用。

回到顶部