本文原创首发于公众号【我做开发那些年】与网站【乔文小屋】,现同步转载至本平台,点击阅读原文

声明:如需转载本文至其他平台,请注明文章来源及公众号信息,感谢您对原创内容的尊重与支持!

说到守护进程,绝大多数开发者其实都不陌生,甚至有些记性比较好的同学还能大段背诵关于进程的面试八股文呢。虽然在日常的Web开发工作中很少使用到它,而且可能从写Web第一天到离职都没有真正写过一个守护进程,即使有或许还是学校里教学用的--使用C语言实现的Demo。

不要怪作者嘴巴毒,事实就是这样的,即使一个工作5,6年的老Web开发,你让他现场写个守护进程,还真不一定就能立马写出来。今天就尝试着使用Node来实现一个守护进程,试着唤醒你那将要死去的记忆。

把一个大象关进冰箱分几步? 写一个守护进程又分几步?

1. 创建子进程并脱离控制终端

当我们使用Node执行某一个js脚本时,Node会创建一个进程,当脚本执行结束了,进程也就结束了。

为了创建守护进程,需要在脚本执行的过程中(父进程)创建一个子进程,并且在子进程创建之后,要让其自立门户,脱离父进程,这样即使父进程退出,也不会影响子进程

// start.js
const { fork } = require('child_process');
const path = require('path'); // 创建子进程
const child = fork(path.join(__dirname, 'daemon.js'), {
detached: true, // 使子进程成为新的进程组领导
stdio: 'ignore' // 忽略标准输入/输出/错误
}); // 解除父进程对子进程的引用
child.unref(); //父进程退出
process.exit(0);

如上述代码所示,使用 chilrd_process中的 fork方法创建一个子进程,它会自动建立父子进程间的 IPC 通信通道。其中的几个参数,详细讲解一下:

  • 第一个参数 daemon.js 是要执行的子进程脚本路径,通过 path.join(__dirname, ...) 确保路径正确。

  • 第二个参数是配置对象,包含关键选项:

    • detached: true​:使子进程成为新进程组的领导者(非 Windows 系统)或拥有独立控制台(Windows),允许子进程在父进程退出后继续运行

    • stdio: ignore​:忽略子进程的标准输入/输出/错误流,避免管道阻塞问题

2. 设置工作目录和文件权限

// daemon.js

// 改变工作目录到根目录或特定目录
process.chdir('/'); // 重设文件权限掩码
process.umask(0);

为了将守护进程独立于启动它的环境,我们通过改变工作目录来实现隔离,而且这样还可以避免挂载点无法卸载。

另外还需要关注一下权限的问题,将掩码设置为0,这样守护进程创建的文件和目录将使用系统的默认权限。

3. 配置日志模块

守护进程不同于在终端执行命令行的进程,它是不占用终端的,所以是看不到它输出内容在终端上。因此,需要配置一个日志模块,用于记录下一些关键信息,避免在报错或者调试的时候两眼抓瞎。

日志模块其实很简单,功能就是将内容记录到文本中即可

// daemon.js

// 设置日志记录
const util = require('util');
const logFile = fs.createWriteStream('daemon.log', { flags: 'a' });
const log = function(msg) {
logFile.write(util.format(msg) + '\n');
};

4. 处理信号和错误

为了提高守护进程的稳定性和可靠性,需要对一些系统信号做处理,从而应对各种意外,毕竟总不希望守护进程挂掉了,而自己却连什么时候挂掉了都不知道吧。

其中,重点是以下几个信号量:

  • SIGINT (Ctrl+C):

    通常由用户在终端中按下 Ctrl+C 发送

  • SIGTERM:

    系统关闭或通过 kill 发送的默认信号

  • SIGHUP:

    传统上表示控制终端关闭,守护进程通常忽略此信号或用于重新加载配置

// 处理进程信号
process.on('SIGINT', () => {
log('收到 SIGINT 信号,准备停止...');
// 执行清理和停止逻辑
}); process.on('SIGTERM', () => {
log('收到 SIGTERM 信号,准备停止...');
// 执行清理和停止逻辑
}); // 忽略挂起信号(SIGHUP)
process.on('SIGHUP', () => {}); // 全局错误处理
process.on('uncaughtException', (err) => {
log(`未捕获异常: ${err}`);
// 可以添加重启逻辑
}); process.on('unhandledRejection', (reason, promise) => {
log(`未处理的Promise拒绝:', ${reason}`);
});

5. 实现守护进程主体内容

前面的一些列操作,都是为了保证守护进程能够正常启动和执行,接下来就到了相对来说简单的部分了。

守护进程主体内容,通常来说最好是个循环,定时任务,或者对外的请求监听,这样才不会运行之后马上就结束。

下面以一个简单的 httpServer作为例子。

const http = require('http');
const port = 5000;
const app = http.createServer((req, res) => {
console.log(`${req.method} ${req.url}`);
res.end('Hello daemon');
}) app.listen(port, '0.0.0.0', () => {
console.log(`Server listening on: http://localhost:${port}`);
})

6. 启动服务并验证

完成代码编写之后,我们尝试着启动服务并验证服务是否启动成功了。打开终端,执行 node start.js。父进程在执行完之后,会立即退出,因此不会占用终端。

通过浏览器访问地址 http://localhost:5000, 能够正确显示内容,说明守护进程启动成功了。

另外查看一下日志文件 daemon.log,可以看到服务启动和请求的记录。

在任务管理中,也能看到一个一直活跃的 node 进程。

Bingo~~~, 一个简单的守护进程就这么实现了。

写在最后

除了上面的方法,其实也还有其它一些快捷的方式来创建守护进程,如使用pm2 或者 forever, 但是纯手工创建守护进程的基本功不能丢了。

实现守护进程的过程,也是深入理解 Node.js 进程模型和操作系统交互的绝佳机会。从工作目录设置、权限管理到信号处理、错误捕获,每一个细节都体现了对系统编程的深刻理解。这些知识不仅适用于守护进程开发,更能帮助我们编写更健壮的 Node.js 应用。

从崩溃到稳定:前端开发者必学的 Node.js 守护进程实战指南的更多相关文章

  1. 【译】在 Chrome 开发者工具中调试 node.js

    原文链接 : Debugging Node.js in Chrome DevTools 原文作者 : MATT DESLAURIERS 译文出自 : 掘金翻译计划 译文链接 : https://git ...

  2. 在 Chrome 开发者工具中调试 node.js

    命令行工具 devtool ,它可以在 Chrome 的开发者工具中运行 Node.js 程序. 下面的记录显示了在一个 HTTP 服务器中设置断点的情况. 该工具基于 Electron 将 Node ...

  3. 学习用Node.js和Elasticsearch构建搜索引擎(6):实际项目中常用命令使用记录

    1.检测集群是否健康. curl -XGET 'localhost:9200/_cat/health?v' #后面加一个v表示让输出内容表格显示表头 绿色表示一切正常,黄色表示所有的数据可用但是部分副 ...

  4. 为什么43%前端开发者想学Vue.js

    根据JavaScript 2017前端库状况调查 Vue.js是开发者最想学的前端库.我在这里说明一下我为什么认为这也是和你一起通过使用Vue构建一个简单的App应用程序的原因. 我最近曾与Evan ...

  5. Web前端入门必学知识

    入门主要有三个部分   一.html+css部分:      1.前端的入门门槛极低,体现在HTML和CSS上运行环境就是浏览器,html+css这部分特别简单,网上搜资料,书籍视频非常多.css中盒 ...

  6. Web 前端开发者必知CSS 属性

    1.  圆角效果 如今的Web设计在不断跟进最新的开发技术,纷纷采用HTML5来开发多样性的Web应用.HTML5的优势之一,就是之前必须用图片实现的元素,现在可以用代码来实现. “border-ra ...

  7. 每个前端开发者必会的 20 个 JavaScript 面试题

    JavaScript 未声明变量直接使用会抛出异常:var name is not defined,如果没有处理异常,代码就停止运行了.但是,使用typeof undeclared_variable并 ...

  8. web前端开发必懂之一:JS继承和继承基础总结

    首先,推荐一篇博客豪情的博客JS提高: http://www.cnblogs.com/jikey/p/3604459.html ,里面的链接全是精华, 一般人我不告诉他; 我们会先从JS的基本的设计模 ...

  9. 【新手必学】Python爬虫之多线程实战

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:清风化煞_   正文 新手注意:如果你学习遇到问题找不到人解答,可以点 ...

  10. 学习用Node.js和Elasticsearch构建搜索引擎(7):零停机时间更新索引配置或迁移索引

    上一篇说到如果一个索引的mapping设置过了,想要修改type或analyzer,通常的做法是新建一个索引,重新设置mapping,再把数据同步过来. 那么如何实现零停机时间更新索引配置或迁移索引? ...

随机推荐

  1. Java编程之面向对象

    一.面向对象 1.定义 (1)类:描述的是具有共性的一类事物 (2)对象:一个个具备了类的特点和功能的个体 (3)面向对象:要完成某件事,首先要先有对象,然后直接调用这个对象的响应功能 2.成员变量: ...

  2. 基于 OT-JSON 与 Immer 设计低代码/富文本场景的状态管理方案

    在复杂应用中,例如低代码.富文本编辑器的场景下,数据结构的设计就显得非常重要,这种情况下的状态管理并非是redux.mobx等通用解决方案,而是需要针对具体场景进行定制化设计,那么在这里我们来尝试基于 ...

  3. NOIP集训 P4137 Rmq Problem / mex 题解

    前置指使:可持久化线段树 题解:P4137 Rmq Problem / mex 有一个长度为 \(n\) 的数组 \(\{ a_1,a_2,...,a_n \}\) . \(m\) 次询问,每次询问一 ...

  4. 集合流之"计算集合中的Integer或Double或BigDecimal的sum总和(累计)"

    一.BigDecimal类型 BigDecimal withdrawalFeeExchange = groupDeverList.stream().map(DevWeekReport::getWith ...

  5. 遇到的问题之“input的值感觉没有设置上去,却有值”

    案例一.批量设置参数 1.被设置的框 改为下拉框的问题可参考:https://www.cnblogs.com/saoge/p/16985318.html <td> <app:inpu ...

  6. 2025年Android面试题含答案

    今年过完年,毫无悬念,成了失业人员之一,于是各种准备面试.前后将近一个月时间,面试10几家公司,基本上80%的企业都拿到了offer.这里面基本上大部分都是小企业居多,少部分中厂,两三家大厂.我并没有 ...

  7. 补充停牌的日K数据

    问题 从TuShare获取的数据,停牌日是没有数据的,这将会在回测时,不能直接参与账户的净值计算,导致账户的净值以及收益计算不准确. 停盘 股票由于某种消息或进行某种活动引起股价的连续上涨或下跌,由证 ...

  8. Longest Univalue Path——LeetCode进阶路

    原题链接https://leetcode.com/problems/longest-univalue-path 题目描述 Given a binary tree, find the length of ...

  9. wireshark 抓包查看包得明文消息

    转载注明出处: 最近在进行一些网络消息得定位,发现可以用wireshark查看网络包得消息内容,特此记录 需要注意得是,需要将wireshark更新到最新得版本,如果是老版本有可能不支持. 使用tcp ...

  10. 前端预览和打印PDF的两种方式

    最近工作中遇到了一个需求,就是前端选择表格中的某一条数据去请求后端接口,后端返回的是一个PDF文件的下载地址,但是需求不希望用户下载下来再去打印,而是直接预览展示,然后就能打印. 一开始按照网上的方式 ...