关于前端:一年Nodejs开发开发经验总结

57次阅读

共计 3445 个字符,预计需要花费 9 分钟才能阅读完成。

本文首发于公众号:合乎预期的 CoyPan

写在后面

人不知; 鬼不觉的,写 Node.js 曾经一年了。不同于最开始的 demo、本地工具等,这一年里,都是用 Node.js 写的线上业务。从一开始的 Node.js 同构直出,到最近的 Node 接入层,也算是对 Node 开发入门了吧。目前,我一个人保护了大部分组内流传下来的 Node 服务,包含外部零碎和线上服务。新增的后盾服务,也是尽可能地应用 Node 进行开发。本文是一下本人最近的一些小小的总结和思考。

本文不会深刻解说 Node.js 自身的个性,架构等等。我也没有写过 Node 扩大或者库什么的,对 Node.js 的理解也并不够深刻。

为何用 Node

对于我来说,对于团队来说,实用 Node 的起因其实很简略:开发起来快。相熟 JS 的前端同学能够很快上手,节省成本。选一个 http server 库起一个 server,抉择适合的中间件,匹配好申请路由,看状况正当应用 ORM 库链接数据库、增删改查即可。

Node 的实用场景

Node.js 应用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。这种模型使得 Node.js 能够防止了因为须要期待输出或者输入(数据库、文件系统、Web 服务器 …)响应而造成的 CPU 工夫损失。所以,Node.js 适宜使用在高并发、I/ O 密集、大量业务逻辑的场景。

对应到平时具体的业务上,如果是外部的零碎,大部分仅仅就是须要对某个数据库进行增删改查,那么 Server 端间接就是 Node.js 一把梭。

对于线上业务,如果流量不大,并且业务逻辑简略的状况下,Server 端也能够齐全应用 Node.js。对于流量微小,复杂度高的我的项目,个别用 Node.js 作为接入层,后盾同学负责实现服务。如下图:

同样是写 JS,Node.js 开发和页面开发有什么区别

在浏览器端开发页面,是和用户打交道、重交互,浏览器还提供了各种 Web Api 供咱们应用。Node.js 次要面向数据,收到申请后,返回具体的数据。这是两者在业务门路上的区别。而真正的区别其实是在于业务模型上(业务模型,这是我本人瞎想的一个词)。间接用图示意吧。

开发页面时,每一个用户的浏览器上都有一份 JS 代码。如果代码在某种状况下崩了,只会对以后用户产生影响,并不会影响其余用户,用户刷新一下即可复原。而在 Node.js 中,在不开启多过程的状况下,所有用户的申请,都会走进同一份 JS 代码,并且只有一个线程在执行这份 JS 代码。如果某个用户的申请,导致产生谬误,Node.js 过程挂掉,server 端间接就挂了。只管可能有过程守护,挂掉的过程会被重启,然而在用户申请量大的状况下,谬误会被频繁触发,可能就会呈现 server 端不停挂掉,不停重启的状况,对用户体验造成影响。

以上,可能是 Node.js 开发和前端 JS 开发最大的区别。

Node.js 开发时的注意事项

用户在拜访 Node.js 服务时,如果某一个申请卡住了,服务迟迟不能返回后果,或者说逻辑出错,导致服务挂掉,都会带来大规模的体验问题。server 端的指标,就是要 疾速、牢靠 地返回数据。

缓存

因为 Node.js 不善于解决简单逻辑(JavaScript 自身执行效率较低),如果要用 Node.js 做接入层,应该防止简单的逻辑。想要疾速解决数据并返回,一个至关重要的点:应用缓存。

例如,应用 Node 做 React 同构直出,renderToString这个 Api,能够说是比拟重的逻辑了。如果页面的复杂度高,每次申请都残缺执行renderToString,会长工夫占用线程来执行代码,减少响应工夫,升高服务的吞吐量。这个时候,缓存就非常重要了。

实现缓存的次要形式:内存缓存。能够应用 Map,WeakMap,WeakRef 等实现。参考以下简略的示例代码:

const cache = new Map();

router.get('/getContent', async (req, res) => {
  const id = req.query.id;
  
  // 命中缓存
  if(cache.get(id)) {return res.send(cache.get(id));
  }
  
  // 申请数据
  const rsp = await rpc.get(id);
     // 通过一顿简单的操作,解决数据
  const content = process(rsp);
  // 设置缓存
  cache.set(id, content);
  
  return res.send(content);
});

应用缓存时,有一个很重要的问题是:内存缓存如何更新。一种最简略的办法,开一个定时器,定期删除缓存,下一次申请到来时,从新设置缓存即可。在上述代码中,减少如下代码:

setTimeout(function() {cache.clear();
}, 1000 * 60); // 1 分钟删除一次缓存

如果 server 端齐全应用 Node 实现,须要用 Node 端间接连贯数据库,在数据时效性要求不太高、且流量不太大的状况下,就能够应用上述相似的模型,如下图。这样能够升高数据库的压力且放慢 Node 的响应速度。

另外,还须要留神内存缓存的大小。如果始终往缓存里写入新数据,那么内存会越来越大,最终爆掉。能够思考应用 LRU(Least Recently Used)算法来做缓存。开拓一块内存专门作为缓存区域。当缓存大小达到下限时,淘汰最久未应用的缓存。

内存缓存会随着过程的重启而全副生效。

当后盾业务比较复杂,接入层流量,数据量较大时,能够应用如下的架构,应用独立的内存缓存服务。Node 接入层间接从缓存服务取数据,后盾服务间接更新缓存服务。

当然,上图中的架构是最简略的情景,事实中还须要思考分布式缓存、缓存一致性的问题。这又是另外一个话题了。

错误处理

因为 Node.js 语言的个性,Node 服务是比拟容易出错的。而一旦出错,造成的影响就是服务不可用。因而,对于谬误的解决非常的重要。

处理错误,最罕用的就是 try catch 了。可是 try catch无奈捕捉异步谬误。Node.js 中,异步操作是非常常见的,异步操作次要是在回调函数中裸露谬误。看一个例子:

const readFile = function(path) {return new Promise((resolve,reject) => {fs.readFile(path, (err, data) => {if(err) { 
                throw err; // catch 无奈捕捉谬误,这和 Node 的 eventloop 无关。// reject(err); // catch 能够捕捉
      }
      resolve(data);
        });
    });
}

router.get('/xxx', async function(req, res) {
  try {const res = await readFile('xxx');
    ...
  } catch (e){
    // 捕捉错误处理
    ...
    res.send(500);
  }
});

下面的代码中,readFile 中 throw 进去的谬误,是无奈被 catch 捕捉的。如果咱们把 throw err 换成 Promise.reject(err),catch 中是能够捕捉到谬误的。

咱们能够把异步操作都 Promise 化,而后对立应用 async、try、catch 来处理错误

然而,总会有中央会被脱漏。这个时候,能够应用 process 来捕捉全局谬误,避免过程间接退出,导致前面的申请挂掉。示例代码:

process.on('uncaughtException', (err) => {console.error(`${err.message}\n${err.stack}`);
});

process.on('unhandledRejection', (reason, p) => {console.error(`Unhandled Rejection at: Promise ${p} reason: `, reason);
});

对于 Node.js 中谬误的捕捉,还能够应用 domain 模块。当初这个模块曾经不举荐应用了,我也没有在我的项目中实际过,这里就不开展了。Node.js 近几年推出的 async_hooks 模块,也还处于试验阶段,不太倡议线上环境间接应用。做好过程守护,开启多过程,谬误告警及时修复,养成良好的编码标准,应用适合的框架,能力进步 Node 服务的效率及稳定性。

写在前面

本文总结了 Node.js 开发一年多以来的实际总结等。Node.js 的开发与前端网页的开发思路不同,着重点不一样。我正式开发 Node.js 的工夫也不算太长,一些点并没有深刻的了解,本文仅仅是一些经验之谈。欢送交换。

正文完
 0