ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PM2 集群模式启动 TCP 服务:从 ecosystem 配置到 Node 负载均衡原理

PM2 集群模式启动 TCP 服务:从 ecosystem 配置到 Node 负载均衡原理 运维CLI可观测性【免费下载链接】pm2Node.js/Typescript/Bun Production Process Manager with a built-in Load Balancer.项目地址https://gitcode.com/gh_mirrors/pm/pm2点击查看免费下载导读本篇文章围绕 PM2 仓库中examples/cluster-tcp示例讲解如何用 PM2 以集群模式cluster mode启动一个基于 Node.jsnet模块的 TCP 服务。你将掌握两种启动方式ecosystem 配置文件与命令行-i max、process.config.js中 cluster/fork 两种运行模式的差异、NODE_APP_INSTANCE实例编号的注入机制以及 PM2 底层如何通过 Nodecluster模块实现负载均衡并最终能独立完成 TCP 长连接服务的多进程部署与验证。示例概览cluster-tcp 目录结构在仓库根目录下的 examples/cluster-tcp 中包含四个文件tcp.js基于net模块的 TCP 服务器是本次集群化部署的核心应用process.config.jsecosystem 配置文件同时定义了 cluster 与 fork 两种运行模式的实例http.js同目录下的 HTTP 对比示例用于直观对比 HTTP 与 TCP 两类服务的集群化差异README.md示例的启动说明原文如下To start tcp application in cluster mode:$ pm2 start ecosystem.config.js # OR $ pm2 start tcp.js -i max下文将围绕这两条命令逐步展开先剖析应用代码与配置文件再追踪底层实现最后给出完整可复现的验证流程。第一步阅读 TCP 应用代码 tcp.jstcp.js 的完整实现如下var net require(net); var server net.createServer(function (socket) { socket.write(Welcome to the Telnet server of the process (process.env.NODE_APP_INSTANCE || must be run on pm2)); }).listen(process.env.PORT || 8888, function() { console.log(Listening on port %s, server.address().port); });这段代码有三处为 PM2 集群化量身设计的关键点端口取自环境变量process.env.PORT || 8888。应用本身不硬编码端口而是由 PM2 在启动时注入PORT环境变量或回退到默认端口 8888。这使同一个脚本可以被多个实例以不同端口运行是实现“多实例不冲突”的前提。实例编号标识process.env.NODE_APP_INSTANCE是 PM2 为每个集群实例自动注入的编号从 0 开始。示例用它把欢迎语输出为 process0、process1 等方便在客户端直接辨认当前连接的是哪个实例。连接即写回每当有客户端建立连接服务器立即向 socket 写入一行欢迎信息。配合telnet或nc即可无代码验证集群分配效果。第二步两种启动方式按示例 README启动集群模式的 TCP 应用有两种等价方式# 方式一通过 ecosystem 配置文件启动 $ pm2 start ecosystem.config.js # 方式二直接指定脚本与实例数 $ pm2 start tcp.js -i max两种方式的语义相同以集群模式启动实例数取 CPU 核心数。区别在于方式一面向“可交付、可复用”的场景所有应用级配置名称、实例数、运行模式、环境变量、日志路径等固化在配置文件中团队成员共享同一份配置即可保证部署一致方式二面向“快速验证”的场景适合临时起一个进程做冒烟测试-i等价于--instances参数直接指定实例数量。关于instances参数的语义可参考 lib/API/schema.jsoninstances: { type: number, docDefault: 1, docDescription: Number of instances to be started in cluster mode }即不指定时默认 1 个实例传入max时 PM2 会按可用 CPU 数量展开。此处exec_mode未显式指定时默认走 fork 模式只有配置了 cluster 模式或命令行带-i才会走集群分支。第三步剖析 process.config.jscluster 与 fork 同台对比process.config.js 是本示例的精华所在它同时声明了两个应用module.exports { apps : [{ name : clustered_tcp, script : ./tcp.js, instances : max, exec_mode : cluster, env : { PORT : 8002 } }, { name : forked_tcp, script : ./tcp.js, env : { PORT : 8001 } }] }逐项解读字段clustered_tcpforked_tcp说明nameclustered_tcpforked_tcp进程在pm2 ls中显示的名字也是日志文件前缀script./tcp.js./tcp.js同一个脚本两种模式对比instancesmax未设置默认 1集群实例数量max表示铺满全部 CPU 核心exec_modecluster未设置默认 fork运行模式决定底层走 Nodecluster还是独立进程env.PORT80028001为每个应用注入独立端口避免多实例端口冲突这个文件展示了一个非常实用的调试技巧同一个 TCP 服务用 cluster 和 fork 两种模式同时跑在不同端口上。客户端分别连接8001、8002端口即可直观对比两种模式的行为差异例如 fork 模式每个进程各自独占监听端口而 cluster 模式多个 worker 共享同一端口由 master 分发。第四步底层原理——PM2 如何实现集群化PM2 的 cluster 与 fork 两种模式在源码中由两个模块分别实现4.1 cluster 模式God.nodeApplib/God/ClusterMode.js 中的God.nodeApp是集群模式的入口。核心动作只有一行——调用 Node 原生cluster.forkvar cluster require(cluster); God.nodeApp function nodeApp(env_copy, cb){ // 透传 node_args 到 cluster.settings.execArgv if (env_copy.node_args Array.isArray(env_copy.node_args)) { cluster.settings.execArgv env_copy.node_args; } // 环境信息以 JSON 字符串形式传给 fork 出的子进程 clu cluster.fork({pm2_env: JSON.stringify(env_copy), windowsHide: true}); // ... };从源码结构可以推断出 PM2 集群模式的核心机制PM2 的 DaemonGod扮演 Node cluster 的 master 角色由它fork出 N 个 workerNode 内置的负载均衡器把到达 master 监听端口的连接按轮询round-robin非 Windows 平台默认分发给各 worker。这正是 PM2 自述 built-in Load Balancer 的落地点。此外源码特意将pm2_env序列化为 JSON 字符串再传入注释解释了 Node cluster 在 fork 进程间传递嵌套对象时会被 toString 化导致args/env结构损坏这是保证 PM2 元数据在 worker 中完整还原的实现细节。4.2 fork 模式God.forkModelib/God/ForkMode.js 的God.forkMode则是另一条路径它通过child_process.spawn以独立进程方式拉起应用。关键逻辑if (interpreter node || RegExp(node$).test(interpreter)) { args.push(path.resolve(..., ProcessContainerFork.js)); }即 fork 模式下 PM2 并不是直接执行用户的tcp.js而是先启动一个包装脚本 lib/ProcessContainerFork.js可选启用 source map 支持、注入自定义模块、发送 node_version 上报再通过require(module)._load(process.env.pm_exec_path, null, true)加载真实应用。这也是 fork 模式进程标题被改写为node pm_exec_path的原因。4.3 两种模式的对比要点维度cluster 模式fork 模式底层机制Nodecluster.forkmaster 统一分发连接child_process.spawn独立进程负载均衡由 Node 内置 Load Balancer 提供无需外部负载均衡器端口占用多 worker 共享同一监听端口各进程需独立端口本例用 PORT 区分适用场景HTTP / 无状态 TCP 服务有状态服务、非常规解释器、需要隔离的场景源码入口lib/God/ClusterMode.jslib/God/ForkMode.js第五步NODE_APP_INSTANCE 是怎么注入的tcp.js 中读取的NODE_APP_INSTANCE由 PM2 在启动每个实例前统一注入实现在 lib/God.js 的God.injectVariables// 允许通过 instance_var 或环境变量覆盖实例编号的键名 var instanceKey process.env.PM2_PROCESS_INSTANCE_VAR || env.instance_var; // 收集同名应用中已使用的实例编号 var instances Object.keys(God.clusters_db) .map(...) .filter(function (proc) { return proc.pm2_env.name env.name ...; }).map(function (proc) { return proc.pm2_env[instanceKey]; }).sort(function (a, b) { return b - a; }); // 默认取 最大编号 1并优先复用空缺编号 var instanceNumber typeof instances[0] undefined ? 0 : instances[0] 1; for (var i 0; i instances.length; i) { if (instances.indexOf(i) -1) { instanceNumber i; break; } } env[instanceKey] instanceNumber;要点编号从 0 开始按同名字应用内的“已用编号 1”递增并会复用因重启、删除而空出来的编号键名默认为NODE_APP_INSTANCE可以通过配置instance_var改名见 lib/API/schema.json 中的instance_var字段默认值为NODE_APP_INSTANCE若同时配置了increment_varPM2 还会注入一个按实例递增的自定义变量schema 中描述为 “inject which increments for each cluster”适合需要每个实例拿到不同编号做分片等场景。第六步启动、验证与运维启动$ cd examples/cluster-tcp $ pm2 start ecosystem.config.js # 或快速启动单个应用铺满 CPU $ pm2 start tcp.js -i max验证集群分配pm2 ls应能看到clustered_tcp展开为多个实例编号 0、1、2…forked_tcp为单实例。使用终端客户端连接验证# 连接集群模式实例端口 8002 $ telnet localhost 8002 # 或 $ nc localhost 8002每次新建立连接时欢迎语中的实例编号process0 / process1 / …会轮换说明连接被 Node 内置负载均衡器分发到了不同 worker。查看各实例日志可进一步确认$ pm2 logs clustered_tcp日志中的 Listening on port 8002 会来自多个不同 PID 的实例但所有 worker 共享同一个监听端口这正是 cluster 模式与 fork 模式的直观差异。运维提示pm2 reload/pm2 restart均可作用于集群应用滚动重启时 Node 的 cluster 机制会逐个替换 worker最大限度降低连接中断集群模式下日志默认按实例拆分可通过merge_logs: true合并见 lib/API/schema.json 中merge_logs字段说明若应用需要固定的外部端口如防火墙白名单可像本示例一样为每个实例注入独立PORT或仅在 cluster 模式中使用单个共享端口。第七步TCP 集群化的注意事项HTTP 与 TCP 在集群化上的一个重要差异值得说明Node 内置负载均衡器对 HTTP 这类短连接、无状态的请求分发非常理想而对于 TCP 长连接连接一旦建立就会固定绑定到某个 worker。因此无状态协议如文本协议、命令响应式服务可直接套用本示例的集群模式有状态协议如需要会话保持的协议需自行在应用层设计粘性会话或共享存储Node 集群机制本身不提供会话保持跨机器水平扩展时Node 内置集群仅覆盖单机多核多机场景仍需外置负载均衡如 LVS、HAProxy 等配合。从当前仓库源码看PM2 集群模式的职责边界正是“单机多核 Node 原生 cluster”这一定位在 lib/God/ClusterMode.js 的注释中也有体现It will wrap the code and enable load-balancing mode。小结通过examples/cluster-tcp示例你可以得到一条完整的 TCP 服务集群化链路配置层用 process.config.js 声明exec_mode: cluster与instances: max用env.PORT区分实例代码层应用通过process.env.PORT与process.env.NODE_APP_INSTANCE感知环境无需任何 PM2 相关依赖原理层lib/God/ClusterMode.js 调用 Nodecluster.fork实现 worker 展开与内置负载均衡lib/God.js 负责实例编号注入验证层pm2 ls、pm2 logs加telnet/nc客户端即可确认多实例轮询分发。如需进一步对比同目录的 http.js 提供了 HTTP 版集群示例监听process.env.PORT || 8000而仓库中更完整的 HTTP 集群演示可参考 examples/cluster-http两相对照即可全面掌握 PM2 的进程管理与负载均衡能力。赞分享运维CLI可观测性【免费下载链接】pm2Node.js/Typescript/Bun Production Process Manager with a built-in Load Balancer.项目地址https://gitcode.com/gh_mirrors/pm/pm2点击查看免费下载相关推荐Conan服务器水平扩展负载均衡与集群配置Conan服务器水平扩展负载均衡与集群配置 为什么需要Conan服务器集群 随着团队规模扩大和项目复杂度提升单一Conan服务器面临三大挑战并发请求瓶颈开发工具包管理器构建工具CLILearn GDScript From Zero社区贡献指南如何参与翻译和代码开发Learn GDScript From Zero社区贡献指南如何参与翻译和代码开发 Learn GDScript From Zero是一个帮助新手从零开始学习人工智能AI 应用桌面应用代码智能体Meshery跨集群服务发现DNS配置与负载均衡策略Meshery跨集群服务发现DNS配置与负载均衡策略 在云原生环境中随着Kubernetes集群数量的增长跨集群服务发现成为保障微服务通信的关键挑战。传统云原生微服务运维DevOps上一篇Laravel Boost终极指南15 MCP工具加速AI辅助开发下一篇DeepSeek Harness 模型提供方配置指南从官方路由、目录提供方到自定义网关与兼容性调优创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表