1.问题

最近 WMS V2 项目 在 Jenkins 上部署前端代码时 build 不成功,如 图1.1 所示:

图1.1 Jenkins 部署过程

查看 部署日志 的时候我们可以看到这个错误,如 图1.2 所示:

图1.2 Jenkins 部署 Log

由此我们可以知道部署失败的主要原因是项目的代码过大,导致内存不够。

2.思考

现在我们知道部署失败是由于 WMS V2 项目代码过大,要从根本上解决这个问题,就是要优化我们的项目代码,减少代码的重复率,提高业务组件和函数的使用率。但是因为这个问题,test 环境的一直不能 build 最新的代码,而优化项目代码并不是一朝一夕的事情,当前的紧急任务是让 test 环境 build 到最新的代码,而且不能对原有代码有非常大的改动,因为有大的改动的话,不能确保是否代码的修改是否会对其它业务造成影响。
那最解决构建内存不够这个问题的最直接的方法就是增大内存
首先我们会想到也许是因为 Node.js 内存不够,在 Node.js 里面,V8 自动帮助我们进行垃圾回收。但我们会思考,Node 进程的 Memory Limit 会是多少呢?在网上我们可以查阅到:

Currently, by default V8 has a memory limit of 512mb on 32-bit systems, and 1gb on 64-bit systems. The limit can be raised by setting —max-old-space-size to a maximum of ~1gb (32-bit) and ~1.7gb (64-bit), but it is recommended that you split your single process into several workers if you are hitting memory limits.

那我们先来看看 Node Process 的 Memory Limit 究竟是多少。最直接的方法就是写一段代码来把内存弄爆,这就知道内存限制是多少了。
代码地址: 代码在这里
我们可以直接用 Node 去运行这段代码,可以在 terminal 看到如图 2.1 所示的结果:

图2.1 Terminal 运行结果

我的电脑是 Macbook Pro masOS Catalina 16GB,Node 版本是 v12.16.1,这段代码大概在 1.6 GB 左右内存时候抛出异常。那我们现在知道 Node Process 确实是有一个内存限制的。那我们就来增大它的内存限制呗。
我们可以用 node —max-old-space-size=6000 来运行这段代码,我们来看看在 terminal 的结果如 图2.2 所示:

图2.2 Terminal 运行结果

一方面我们可以尝试在 build 的时候增大内存老生代区域的大小,我们修改 deploy 文件夹下的部署文件 build 时候的 commands,如图 2.3 所示:

图2.3 部署文件

另一方面因为我们的项目是用 Vue 的脚手架搭建起来的,webpack 的实现都是封装好的了。这时我们可以从另一个方向入手 - webpack plugin,或许可以通过 plugin 来修改 build 的时候的内存大小。
在 vue.config.js 里面我们引入 fork-ts-checker-webpack-plugin,通过它修改内存限制的大小,代码如图2.4所示:

configureWebpack: config => { // get a reference to the existing ForkTsCheckerWebpackPlugin const existingForkTsChecker = config.plugins.filter( p => p instanceof ForkTsCheckerWebpackPlugin, )[0]; // remove the existing ForkTsCheckerWebpackPlugin // so that we can replace it with our modified version config.plugins = config.plugins.filter( p => !(p instanceof ForkTsCheckerWebpackPlugin), ); // copy the options from the original ForkTsCheckerWebpackPlugin // instance and add the memoryLimit property const forkTsCheckerOptions = existingForkTsChecker.options; forkTsCheckerOptions.memoryLimit = 4096; config.plugins.push(new ForkTsCheckerWebpackPlugin(forkTsCheckerOptions)); },

图2.4 vue.config.js 文件

这时候在 Jenkins 上 build 项目的时候就成功了。

在 vue.config.js 里面我们引入 fork-ts-checker-webpack-plugin 后我们就可以成功构建我们。是因为 fork-ts-checker-webpack-plugin 会在单独的进程里做 Typescript 类型检测和 ESLint 检查(by moving each to a separate process),不仅减少了在一个进程里的内存开销,而且加快了项目的构建速度,而 Typescript 类型检测是非常耗费内存的。

3.总结

现在 WMS V2 项目 在 Jenkins 上部署前端代码时 build 不成功,部署失败是由于 WMS V2 项目代码过大。为了可以让项目构建成功,而且不对代码进行大的改动。临时的一种方式是增大项目的构建内存。而项目构建时 Typescript 类型检测是非常耗费内存的。在 vue.config.js 里面我们引入 fork-ts-checker-webpack-plugin,fork-ts-checker-webpack-plugin 会在单独的进程里做 Typescript 类型检测和 ESLint 检查(by moving each to a separate process),不仅减少了在一个进程里的内存开销,而且加快了项目的构建速度。

当然项目构建失败的根本原因是项目代码过大,要从根本上解决这个问题,就是要优化我们的项目代码,减少代码的重复率,提高业务组件和工具函数的使用率。这也是我们日后优化的重心。

4.分享

在这个查阅过程中,看到一些有意思的文章,有兴趣的同事可以去看看:

  1. Understanding Garbage Collection and Hunting Memory Leaks in Node.js
  2. A tour of V8: Garbage Collection
  3. Hunting a Ghost - Finding a Memory Leak in Node.js