在开发服务端应用时,经常需要获取运行环境的底层信息,比如监控服务器的 CPU 负载、查看剩余内存、判断当前操作系统类型、或者获取本机的 IP 地址。Node.js 的 os 模块提供了这一系列与操作系统交互的 API,无需安装任何第三方包即可使用。
os 模块的方法虽然不多,但覆盖了日常开发中绝大部分对系统信息的需求。本节将重点讲解 CPU、内存、操作系统类型和网络接口四个最常用的信息获取方式,并穿插实际应用场景,帮助你快速将这些能力集成到自己的项目中。
8.3.1 CPU 信息获取
os.cpus() 方法会返回一个数组,数组中的每个元素对应一个逻辑 CPU 核心(包括超线程)。每个核心的信息对象包含以下字段:
model:CPU 型号字符串speed:CPU 主频,单位 MHztimes:一个对象,包含user、nice、sys、idle、irq等数字,分别代表该核心在不同状态下花费的时间(单位毫秒,但受系统时钟限制,不一定精确)
简单调用示例如下:
const os = require('os');
const cpus = os.cpus();
console.log(`逻辑 CPU 核心数:${cpus.length}`);
cpus.forEach((cpu, index) => {
console.log(`核心 ${index}: ${cpu.model} @ ${cpu.speed} MHz`);
});
在一台 4 核 8 线程的 i7 机器上,可能会输出类似:
逻辑 CPU 核心数:8
核心 0: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz @ 1992 MHz
核心 1: ...
CPU 信息的实用价值:
- 负载判断与集群策略:
cluster模块通常会根据os.cpus().length决定启动多少个子进程。我们也可以利用times字段计算出单个核心的空闲率(idle / total),再配合os.loadavg()判断整体负载,从而决定是否需要动态扩容。 - 监控与告警:搭建自己的监控面板时,把每个核心的型号、主频、以及实时计算的利用率暴露出去,便于运维人员掌握服务器状态。
- 区分开发和生产环境:偶尔可能会根据 CPU 型号做软性判断,但一般不推荐硬编码逻辑,更建议用环境变量区分。
需要留意的是,times 对象里的值是累积时间,要计算真实占用率,需要两次采样相减后再做除法。下面是一个简单的 CPU 使用率计算示例:
function getCPUUsage(sampleTime = 1000) {
return new Promise(resolve => {
const start = os.cpus().map(cpu => cpu.times);
setTimeout(() => {
const end = os.cpus().map(cpu => cpu.times);
const usage = end.map((core, i) => {
const totalStart = Object.values(start[i]).reduce((a, b) => a + b, 0);
const totalEnd = Object.values(core).reduce((a, b) => a + b, 0);
const idleDiff = core.idle - start[i].idle;
return 1 - idleDiff / (totalEnd - totalStart);
});
resolve(usage);
}, sampleTime);
});
}
getCPUUsage().then(console.log); // [0.05, 0.12, ...] 各核心使用率
8.3.2 内存信息获取
内存信息通过两个方法获取:
os.totalmem():以字节为单位返回系统总内存容量os.freemem():以字节为单位返回当前空闲内存容量
两者返回的都是整数,需要手动转换为更可读的单位:
const totalMem = os.totalmem();
const freeMem = os.freemem();
const usedMem = totalMem - freeMem;
function formatBytes(bytes) {
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let value = bytes;
let unitIndex = 0;
while (value >= 1024 && unitIndex < units.length - 1) {
value /= 1024;
unitIndex++;
}
return `${value.toFixed(2)} ${units[unitIndex]}`;
}
console.log(`总内存:${formatBytes(totalMem)}`);
console.log(`空闲内存:${formatBytes(freeMem)}`);
console.log(`已用内存:${formatBytes(usedMem)}`);
console.log(`使用率:${((usedMem / totalMem) * 100).toFixed(2)}%`);
应用场景:
- 健康检查端点:很多服务会在
/health接口中返回当前内存使用比例,当超过阈值(如 90%)时,负载均衡器可以暂时停止向该节点转发流量。 - 内存溢出预警:配合进程监控工具(如 PM2),当系统可用内存持续下降时主动记录日志或发送告警,帮助排查是否有内存泄漏。
- 自动降级策略:内存紧张时,可主动清理缓存、拒绝大文件上传等,防止进程 OOM 崩溃。
os.freemem() 返回的是系统全局的空闲内存,并不是 Node.js 进程自身的内存占用。后者可以通过 process.memoryUsage() 获得,两者结合可以更全面地评估资源状况。
8.3.3 操作系统信息
os 模块提供了一系列方法用于获取操作系统的类型、版本和架构:
os.type():返回操作系统类型,如'Linux'、'Darwin'(macOS)、'Windows_NT'os.platform():返回更具体的平台标识,如'linux'、'darwin'、'win32',常用于跨平台工具的条件判断os.release():返回操作系统的发行版本号,例如'5.4.0-66-generic'os.arch():返回 CPU 架构,如'x64'、'arm'、'arm64'os.version():返回内核版本字符串(Linux 下更详细)os.hostname():返回主机名os.homedir():返回当前用户的主目录路径os.tmpdir():返回系统临时文件夹路径
综合使用示例:
console.log('操作系统类型:', os.type());
console.log('平台标识:', os.platform());
console.log('发行版本:', os.release());
console.log('CPU 架构:', os.arch());
console.log('主机名:', os.hostname());
console.log('用户目录:', os.homedir());
console.log('临时目录:', os.tmpdir());
实际开发中的用途:
- 跨平台兼容:在用 Node.js 编写 CLI 工具或构建脚本时,经常要根据
os.platform()来决定执行的命令(如 Windows 上用copy,UNIX 上用cp)或路径分隔符。 - 条件加载原生模块:有些 npm 包会依据
os.arch()选择不同的预编译二进制文件。 - 环境检测与日志记录:在启动应用时打印操作系统信息,方便日后回溯问题时确定运行环境。
- 路径拼接:虽然
path模块会处理平台差异,但在手动处理配置路径时,os.homedir()常用于指向用户主目录下的配置文件(如~/.myapp/config.json)。
注意:os.type() 和 os.platform() 同名方法的区别在 Node.js 早期版本中可能存在细微差异,现在基本可以互换,但 platform() 的值更简洁,通常推荐用它做逻辑判断。
8.3.4 网络接口信息
在多网卡服务器、容器环境或者需要获取本机 IP 的场景中,os.networkInterfaces() 是极为常用的方法。它返回一个对象,键是网络接口名称(如 'lo'、'eth0'、'WLAN'),值是一个数组,因为一个接口可能同时绑定 IPv4 和 IPv6 地址。
每个地址项包含:
address:IP 地址(IPv4 或 IPv6)netmask:子网掩码family:'IPv4'或'IPv6'mac:MAC 地址(物理地址)internal:布尔值,true表示是内部回环接口(如127.0.0.1)cidr:CIDR 表示法(Node.js v18 以上)scopeid:IPv6 的作用域 ID
获取所有网络信息:
const interfaces = os.networkInterfaces();
console.log(interfaces);
通常我们更关心如何获取本机的局域网 IP(IPv4),用于在其他服务中注册自己的地址。一个常见的工具函数如下:
function getLocalIP() {
const interfaces = os.networkInterfaces();
for (const name of Object.keys(interfaces)) {
for (const iface of interfaces[name]) {
// 跳过内部地址和非 IPv4
if (iface.family === 'IPv4' && !iface.internal) {
return iface.address;
}
}
}
return '127.0.0.1';
}
console.log('本机 IP:', getLocalIP());
在 Docker 容器内,网络接口名称通常类似 eth0,也可能有多个虚拟网卡。上述函数会返回第一个非内部的 IPv4 地址,基本满足需求。如果有更复杂的需求(如根据网卡名称或特定子网筛选),可以在循环中加入额外判断。
真实应用场景:
- 服务注册与发现:微服务启动时,向注册中心(如 Consul、Eureka)上报自己的 IP 和端口。
os.networkInterfaces()可以动态拿到真实 IP,无需硬编码。 - 健康检查:暴露服务状态时附带本机 IP,便于运维工具区分不同节点。
- 调试信息:在启动日志中打印本机 IP,方便本地联调时快速复制地址给前端或其他同事。
- 安全控制:有的内网服务可能只允许指定网段的请求,通过检查请求来源 IP 是否在某个
netmask范围内做权限控制(需手动计算,或结合ip等库)。
注意:os.networkInterfaces() 每次调用都会重新获取信息,开销很小但也不是完全免费,如果频繁调用(如每次请求都获取 IP)建议缓存结果。另外,在多 IP 环境中要明确业务需要哪个 IP(如内网通信用 eth0,服务对外暴露可能用公网地址),必要时可通过环境变量强制指定。
8.3.5 其他实用方法
os 模块还提供了一些辅助方法,虽然使用频率稍低,但在特定场景中非常方便:
os.uptime():返回系统运行时间(秒),可用于计算服务启动了多久,或者定期打印系统 uptime 到监控。os.loadavg():返回一个包含 1、5、15 分钟平均负载的数组(仅 UNIX/Linux 和 macOS 下有意义,Windows 上会返回[0, 0, 0])。负载是系统压力最直接的指标,当 1 分钟负载接近 CPU 核心数时,说明系统已经满负荷。os.userInfo():返回当前用户的信息对象,包含username、uid、gid、shell、homedir等。可用于获取当前启动用户的身份,或配置个性化的路径。os.endianness():返回 CPU 字节序,'BE'或'LE'。处理二进制协议时可能需要用到,但现代桌面和服务器端基本全都是小端序(LE)。os.EOL:返回操作系统的默认行末符(\n或\r\n),在写文件或日志时很有用,确保在不同平台上用正确的换行。
综合运用这些方法,可以很方便地构建一个系统信息摘要:
const info = {
platform: os.platform(),
hostname: os.hostname(),
arch: os.arch(),
cpus: os.cpus().length,
memory: {
total: formatBytes(os.totalmem()),
free: formatBytes(os.freemem()),
},
uptime: `${Math.floor(os.uptime() / 3600)} 小时`,
loadavg: os.loadavg(),
network: getLocalIP(),
};
console.log(info);
这个摘要可以直接作为健康检查接口的响应体,帮助运维快速了解当前实例的状态。
8.3.6 实际应用:一个简单的系统健康检查接口
将上述所有技能结合在一起,我们可以为任何 Node.js 服务快速添加一个轻量级的健康检查端点。以下是一个基于 Express 的示例:
const express = require('express');
const os = require('os');
const app = express();
function getHealthStatus() {
const usedMemRatio = 1 - os.freemem() / os.totalmem();
const cpuUsage = os.loadavg()[0] / os.cpus().length; // 1分钟负载 / 核心数
const status = (usedMemRatio < 0.9 && cpuUsage < 0.7) ? 'ok' : 'warning';
return {
status,
timestamp: new Date().toISOString(),
hostname: os.hostname(),
uptime: os.uptime(),
cpu: {
cores: os.cpus().length,
loadAvg1m: os.loadavg()[0],
usageRatio: cpuUsage.toFixed(2),
},
memory: {
total: os.totalmem(),
free: os.freemem(),
usageRatio: usedMemRatio.toFixed(2),
},
network: os.networkInterfaces()['eth0']?.[0]?.address || '未知',
};
}
app.get('/health', (req, res) => {
res.json(getHealthStatus());
});
app.listen(3000, () => console.log('Health check on port 3000'));
这样,当负载均衡器或 K8s 探活访问 /health 时,就能获得实时的 CPU、内存、网络和运行时长信息,并判断节点是否健康。真实环境下还可以加入数据库连接检查、磁盘剩余空间等更多指标。
os 模块以极轻量的方式提供了系统底层信息的访问能力,是构建 DevOps 工具、运维脚本、服务监控的基础。它的 API 简洁可靠,无需任何第三方依赖,在性能敏感、追求启动速度的场景下尤其适用。在后续的章节中,我们将继续探讨与进程管理和集群模式相关的模块,这些模块常常需要依赖 os 获取的信息来做智能调度。