• 作者:老汪软件技巧
  • 发表时间:2024-10-14 11:03
  • 浏览量:

起因

之前写过一篇 Nuxt3 的文章,Nuxt3 环境变量配置,用到了 PM2,但是里面的一些配置存在问题,最近有空又验证了一下,这里做一个勘误。

问题

PM2 的启动配置中有一项是exec_mode,默认是fork,另一个可选值是cluster,fork 是单进程模式,cluster 是多进程模式,也就是常说的集群模式。

最早开始用 Nuxt3 的时候,还在内测阶段,当时找到的资料很多都是基于 Nuxt2 的,Nuxt3 的文档也不是很全,所以很多配置都是参考 Nuxt2 的,有点病急乱投医了,最后拼凑出一套能正常启动的配置就没有再调整了,详情可以看之前的文章,这里把有问题的字段挑出来。

{
  exec_mode: "cluster",
  instances: "max", // Or a number of instances
  script: "npm",
  args: "start",
}

这里的配置造成了什么问题呢?首先可以看出来是通过 npm 来启动程序的,通过npm run start这个命令来执行package.json中的start脚本node .output/server/index.mjs,首先这样是可以启动的,但是存在一些问题:

服务器偶尔会出现 CPU 占用过高的情况,查看 CPU 占用情况发现是 node 进程,于是定位到 PM2,查看运行情况发现多个进程只有一个启动成功,其他的进程都在反复重启,CPU 自然升高。再去查看日志发现是因为端口被占用导致只有一个进程能启动成功,这时我意识到配置可能有问题,不过当时没有时间调整,于是一直以来都是通过手动停止其他进程来解决问题。

解决

有问题当然要解决,集群模式我接触的不多,但既然有这个配置,肯定不会因为端口占用就无法启动,Nuxt3 至今已有较大的版本变化,于是我再次翻看 Nuxt3 的文档,看下官方是怎么配置 PM2 的:

docs/getting-started/deployment

module.exports = {
  apps: [
    {
      name: "NuxtAppName",
      port: "3000",
      exec_mode: "cluster",
      instances: "max",
      script: "./.output/server/index.mjs",
    },
  ],
};

最早我是没觉得问题出在启动脚本这的,直到我又找到一个 github 上的 issue Port Already in Use in Cluster Mode,包括里面关联的问题都提到了不应使用 npm 来启动,而是直接使用脚本路径。

You can't cluster application via npm start. To make cluster work, in the script attribute of your ecosystem.config.js, directly put the path of your javascript app.

通过 npm 启动时并没有将这个进程直接托管给 pm2,而是通过 npm 启动了一个进程,pm2 只是监控这个进程,所以 pm2 并不能很好地管理这个进程,上面的截图中也可以看到版本号是 0.39.7,其实这个是服务器上 nvm 的版本,而不是 nuxt3 程序的版本。

关于集群模式

最早我怀疑是集群模式的问题,但即使改成 fork,也还是会出现端口占用的报错,虽然可以通过修改instances来解决,但隐约觉得不是正确的方法,在解决启动报错问题之后,我再次尝试了使用集群模式,不过我仍然不太清楚是否应该使用这个配置,于是我做了一些测试,只是在本地环境的一些测试,结果并不准确,只能做个参考。

instances是要使用的进程数量,它的值可以是'max'、-1或其他数字,'max'表示使用最大的实例数,-1表示使用最少的基本就是 1 个,其他数字表示使用指定数量的实例数,这个实例数是根据 CPU 核心数来计算的,比如单核双线程的 CPU,实例数就是 2。

首先我在本地启动了一个 nuxt3 项目,这里模拟服务器环境只用了 2 个实例,然后使用两种不同的 pm2 启动方式执行。

使用pm2 monit监控进程,可以看到这时还没什么区别,这时用 jmeter 进行压测,创建 500 个线程访问集群模式的进程,可以看到两个进程都在运行:

然后同样的压测,但是使用 fork 模式,可以看到只有一个进程在运行:

我并不了解压测,只是简单对比一下,可以看出集群模式的 CPU 波动还是比较明显的,fork 模式 CPU 的波动不明显,但是内存占用会极速攀升,没有截到图。继续增加压测线程,内存的变化并不明显,集群模式的 CPU 占用会更加明显,也有可能是因为压测应用和被压测应用都在本地,所以这个测试结果并不准确,在错误率方面,也没有测出两者的区别。通过简单的测试并没有感受出优劣,集群的优势可能体现在性能和容错方面,在硬件上看不出太多效果,这里只是做一个记录,具体还需要更多的实践经验。

集群模式允许联网的 Node.js 应用程序(http(s)/tcp/udp 服务器)在所有可用的 cpu 上扩展,无需任何代码修改。根据可用 cpu 的数量,这将极大地提高应用程序的性能和可靠性。