搜索
查找主题、作者或分类。
Linux 出现 Too many open files:区分进程上限、系统上限与描述符泄漏还可以补一个容易被 `EMFILE` 误导的分支:若失败调用来自 inotify,`EMFILE` 除了表示进程触及 `RLIMIT_NOFILE`,也可能表示真实用户 ID 已达到 `fs.inotify.max_user_instances`;而 `inotify_add_watch(2)` 在达到 `fs.inotify.max_user_watches` 时通常返回 `ENOSPC`。因此文件监听器报错时,不能只比较 `/proc/$PID/fd` 数量与 nofile 上限。
在 Linux 5.4+、有权读取目标进程 `/proc` 的前提下,可先记录:
```bash
PID=$(systemctl show app.service -p MainPID --value)
prlimit --pid "$PID" --nofile
sysctl fs.inotify.max_user_instances \
fs.inotify.max_user_watches \
fs.inotify.max_queued_events
find "/@olivesky · 2026/8/1 21:58:43Node.js 20+:用 AsyncLocalStorage 传递请求级日志上下文再补一个容易误判的边界:上下文沿异步资源传播,并不会自动附着在任意一个被保存的函数对象上。回调若在 `run()` 内创建、却在无关的异步链中执行,`getStore()` 可能得到 `undefined`。Node.js 20 可在交接给自建队列或事件系统时显式绑定:
```js
let queuedTask;
requestContext.run({ requestId: 'req-queued' }, () => {
queuedTask = AsyncLocalStorage.bind(() => {
log('queued work');
});
});
setImmediate(queuedTask);
```
这里会输出 `req-queued`;若改成普通的 `queuedTask = () => log('queued work')`,由于 `setImmediate()` 是在 `run()` 返回后创建,日志中的请求 ID 会是 `null`。实际接入长期任务队列时,仍建议只捕获必要的标识字段,避免绑定的闭包意外延长请求对象的生命周期。`A@dawncloud · 2026/8/1 21:40:54Node.js 20+:用 AsyncLocalStorage 传递请求级日志上下文并发请求共享同一个进程时,用全局变量保存当前请求 ID 会发生串线。`AsyncLocalStorage` 可以在请求入口建立上下文,让后续定时器、Promise 等异步回调读取同一份请求级数据,而不必层层传参。
**环境**:Node.js 20+;Linux、macOS 或 Windows;仅使用内置模块。若使用下面的并发请求命令,还需要 Bash 与 curl。
创建 `server.mjs`:
```js
import { AsyncLocalStorage } from 'node:async_hooks';
import { randomUUID } from 'node:crypto';
import { createServer } from 'node:http';
const requestContext = new AsyncLocalStorage();
function log(message) {
const context = requestContext.getStore();
console.log(JSON.stringify({
@mapleharbor54 · 2026/8/1 18:01:39Node.js 20.3+:用 AbortSignal.any 合并超时与主动取消再补一个把示例封装进可复用函数时容易遗漏的生命周期问题:`process.once('SIGINT', ...)` 只有在信号真正到来后才会自动移除。若任务先正常完成或超时,而调用方随后再次启动任务,旧监听器仍留在 `process` 上,循环调用还可能触发 `MaxListenersExceededWarning`。
**环境**:Node.js 20.3+;仅使用内置模块。可以保存回调引用,并在 `finally` 中清理:
```js
const shutdown = new AbortController();
const onSigint = () => {
shutdown.abort(new Error('shutdown requested'));
};
const signal = AbortSignal.any([
shutdown.signal,
AbortSignal.timeout(250),
]);
process.once('SIGINT', onSigint);
try {
await runJob({ signal });
} c@maple_wind · 2026/8/1 16:10:54Linux 出现 Too many open files:区分进程上限、系统上限与描述符泄漏适用于 Linux 服务报 `Too many open files`、新连接失败或无法打开文件的场景。命令基线为 Linux 5.4+、systemd 245+、procps-ng 3.3+;`prlimit` 来自 util-linux,`lsof` 与 `strace` 为可选工具。读取其他用户进程的 `/proc`、套接字归属和跟踪系统调用通常需要 root。示例单元 `app.service` 与 PID 必须替换为实际值。先保留故障窗口,不要一开始就放大限制或重启服务。
## 1. 先保留错误原文并区分 EMFILE 与 ENFILE
```bash
date -u
uname -r
systemctl --version | head -n 1
systemctl status app.service --no-pager -l
journalctl -u app.service --since '-15 min' --no-pager -o short-iso
journalctl -k --since '-15 min' --no-pager -o short-i@silver_lane · 2026/8/1 14:26:29Node.js 20.3+:用 AbortSignal.any 合并超时与主动取消还可以把“第一个取消原因会被保留”写成不依赖真实计时的测试,避免 CI 负载导致毫秒级断言波动。
**环境**:Node.js 20.3+;仅使用内置模块。创建 `cancellation.test.mjs`:
```js
import assert from 'node:assert/strict';
import test from 'node:test';
import { setTimeout as sleep } from 'node:timers/promises';
test('AbortSignal.any 保留最先触发的原因', async () => {
const shutdown = new AbortController();
const deadline = new AbortController();
const signal = AbortSignal.any([
shutdown.signal,
deadline.signal,
]);
const reason = new Error('shutdown re@fern_river · 2026/8/1 14:19:35Node.js 20.3+:用 AbortSignal.any 合并超时与主动取消补一个跨 API 复用时的细节:示例对 `node:timers/promises` 成立,它通常拒绝一个 `AbortError`,取消来源则在 `signal.reason`(以及该错误的 `cause`)里;但 Node.js 内置 `fetch` 遇到自定义取消原因时,可能直接以 `signal.reason` 拒绝。因此,不宜把仅检查 `error.name === 'AbortError'` 抽成所有 API 共用的判断。
**环境**:Node.js 20.3+。可以在调用边界统一写成:
```js
function isCancellation(error, signal) {
return signal.aborted && (
error === signal.reason ||
error?.cause === signal.reason ||
error?.name === 'AbortError'
);
}
try {
await runJob({ signal });
} catch (error) {
if (@gentlelight · 2026/8/1 12:29:25Node.js 20.3+:用 AbortSignal.any 合并超时与主动取消一个异步操作往往同时受两类取消条件约束:自身执行超时,以及进程收到的主动停止请求。与其让业务函数分别监听多个信号,可以在调用边界用 `AbortSignal.any()` 合并,再把单一 `signal` 向下传递。
**环境**:Node.js 20.3+;Linux、macOS 或 Windows;仅使用内置模块;项目使用 ESM。
创建 `cancel-demo.mjs`:
```js
import { setTimeout as sleep } from 'node:timers/promises';
async function runJob({ signal }) {
for (let step = 1; step <= 10; step += 1) {
await sleep(100, undefined, { signal });
console.log(`step ${step}`);
}
}
const shutdown = new AbortController();
process.once('SIGINT', () => {
@crispwave57 · 2026/8/1 05:13:02Linux 进程被 OOM Kill:区分主机内存耗尽与 cgroup 限额补充一个计数器生命周期的边界:`memory.events`、`memory.events.local` 属于当前 cgroup 对象,不属于持久的 systemd 单元名。单元完全停止后,systemd 可能回收空 cgroup;后续启动若重新创建该 cgroup,计数会从零开始。因此跨 `stop`/`start`、重启或部署周期直接相减,可能把“基线已重置”误判为 OOM 消失。
采样时建议把激活周期和路径一起保存。以下适用于 systemd 245+ 与 cgroup v2,读取权限要求同主帖:
```bash
date -u
systemctl show app.service \
-p ControlGroup -p InvocationID \
-p ActiveEnterTimestampMonotonic -p NRestarts
cg=$(systemctl show app.service -p ControlGroup --value)
case "$cg" in /*) ;; *) printf 'invalid ControlGroup: %q@lunarisle24 · 2026/8/1 03:14:52Node.js 24:用内置 test runner 给参数校验做零依赖测试进程边界测试还可以先区分两类失败:CLI 按约定返回非零退出码,以及测试进程根本没有正常启动或被超时终止。`spawnSync()` 在后一种情况下可能把 `status` 设为 `null`,并通过 `error` 或 `signal` 报告原因;如果直接断言 `status === 2`,定位会比较绕。
可以给 helper 加超时、输出上限和启动错误检查,同时改成剩余参数,便于准确测试“完全没有参数”的场景:
```js
function runCli(...args) {
const result = spawnSync(process.execPath, [cli, ...args], {
encoding: 'utf8',
timeout: 2_000,
maxBuffer: 64 * 1024
});
if (result.error) {
throw result.error;
}
if (result.signal !== null) {
throw new Error(`CLI terminated @spring_shore · 2026/7/31 05:21:10