NodeJS源码分析(3)

本文详细介绍了Node.js中的Stream模块,包括其基本概念、使用场景及如何通过Stream优化文件读写性能。同时,文章还讲解了如何利用管道流进行多文件间的链式操作,以及Readable流的不同工作模式。

Node Stream模块

Stream在平时业务开发时很少用到, 但是很多模块都是基于stream实现的,引用官方文档的解释:

流(stream)在 Node.js 中是处理流数据的抽象接口(abstract interface)。

stream 模块提供了基础的 API 。使用这些 API 可以很容易地来构建实现流接口的对象。

流可以是可读的、可写的,或是可读写的。所有的流都是 EventEmitter 的实例。

为什么应该使用Stream

先来看一段代码:

这段代码有什么问题, 看似是没有问题的。

如果data.txt文件体积非常大,nodejs读入内存当中,然后全部取出

这样会对性能造成很大影响

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    fs.readFile(__dirname + '/data.txt', function (err, data) {
        res.end(data);
    });
});
server.listen(8000);

经过优化后代码如下:

这段将data.txt一段一段的发送到用户端

这样减少了很多的服务器压力

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    let stream = fs.createReadStream(__dirname + '/data.txt');//创造可读流
    stream.pipe(res);//将可读流写入response
});
server.listen(8000);

管道流Pipe

管道提供了一个输出流到输入流的机制, 从获取到数据传入另外一个流
无论哪一种流,都会使用.pipe()方法来实现输入和输出。

读取input.txt文件流

hello world

var fs = require("fs");

// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');

// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');

// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);

console.log("程序执行完毕");

查看输出文件流output.txt

hello world

如果多文件还可进行链式操作:

代码如下:

a.pipe(b);
b.pipe(c);
c.pipe(d);

//上面代码等价于这样
a.pipe(b).pipe(c).pipe(d)

readable 可读操作

Readable流可以产出数据流,你可以将这些数据传送到一个writable,transform, duplex,并调用pipe()方法:

var Readable = require('stream').Readable;

var rs = new Readable;
rs.push('beep ');
rs.push('boop\n');
rs.push(null);

rs.pipe(process.stdout); //输出: beep boop

在上面的代码中rs.push(null)的作用是告诉rs输出数据应该结束了。

var Readable = require('stream').Readable;
var rs = Readable();

var c = 97;
rs._read = function () {
    rs.push(String.fromCharCode(c++));
    if (c > 'z'.charCodeAt(0)) rs.push(null);
};

rs.pipe(process.stdout);//输出 abcdefghijklmnopqrstuvwxyz

还可以通过监听事件readable,触发时手工读取chunk数据:

一旦注册了readable事件,必须手工读取read数据,否则数据就会流失

var Read = require('stream').Readable;
var r = new Read();

r.push('hello');
r.push('world');
r.push(null);

r.on('readable', function () {
    var chunk = r.read();
    console.log('get data by readable event: ', chunk.toString())
});

// get data by readable event:  hello world!
注意:process.stdout之前已经将内容推送进readable流rs中,但是所有的数据依然是可写的

Readable Stream的模式

Readable Stream 存在两种模式(flowing mode 与 paused mode),
这两种模式决定了chunk数据流动的方式—自动流动还是手工流动。那如何触发这两种模式呢:

flowing mode: 注册事件data、调用resume方法、调用pipe方法

paused mode: 调用pause方法(没有pipe方法)、移除data事件 && unpipe所有pipe

// data事件触发flowing mode
Readable.prototype.on = function(ev, fn) {
    ...
    if (ev === 'data' && false !== this._readableState.flowing) {
        this.resume();
      }
      ...
}

// resume触发flowing mode
Readable.prototype.resume = function() {
    var state = this._readableState;
    if (!state.flowing) {
           debug('resume');
           state.flowing = true;
    resume(this, state);
  }
  return this;
}

// pipe方法触发flowing模式
Readable.prototype.resume = function() {
    if (!state.flowing) {
        this.resume()
    }
}

结论

两种方式取决于一个flowing字段:true –> flowing mode;false –> paused mode

三种方式最后均是通过resume方法,将state.flowing = true

<think>好的,我现在需要帮助用户找到关于如何使用Node.js源码搭建服务器系统的教程和最佳实践。首先,我得仔细分析用户的需求。用户提到了引用内容,其中引用[1]涉及Node.js的安装、配置和运行,而引用[2]是关于用Node.js和Express搭建博客系统的实例。看起来用户可能已经有一些基础,现在想深入搭建服务器系统,可能需要更详细的步骤和最佳实践。 首先,我需要确保覆盖用户提到的教程和最佳实践。用户可能需要从环境搭建开始,包括安装Node.js、配置npm、设置镜像源等,这部分引用[1]已经提到,可以作为基础。然后,用户可能需要实际的服务器搭建步骤,比如使用Express框架,处理路由、中间件、静态资源等,这部分引用[2]中的博客系统可以作为参考案例。 接下来,我需要考虑用户可能的实际应用场景。搭建服务器系统可能涉及创建HTTP服务器、处理请求和响应、使用中间件、连接数据库、部署等。因此,回答需要分步骤,结构清晰,每个步骤给出代码示例和解释,同时提到最佳实践,比如错误处理、环境变量配置、安全措施等。 还要注意用户提到的系统级指令,比如正确使用LaTeX格式,生成相关问题,中文回答等。需要确保所有数学公式正确使用$...$或$$...$$,例如在讲解性能优化时可能需要提到时间复杂度O(n log n),这时候需要用LaTeX格式。 生成的相关问题需要与主题相关,可能涉及框架选择、性能优化、安全措施、部署工具等,帮助用户进一步探索。 需要检查是否有遗漏的步骤,比如是否提到使用TypeScript,或者如何测试API,但根据引用内容,用户可能更关注基础搭建,所以重点放在Express、路由、中间件、数据库连接、部署等方面。 最后,确保回答引用正确的来源,比如在提到环境配置时引用[1],在提到实际项目扩展时引用[2]。同时,语言要简洁明了,步骤清晰,代码示例正确,符合最佳实践。</think>### 使用 Node.js 源码搭建服务器系统的教程与最佳实践 #### 一、环境准备与基础配置 1. **安装 Node.js** 从[官网下载](https://nodejs.org)对应系统的安装包,建议选择 LTS 版本[^1]。安装完成后验证版本: ```bash node -v npm -v ``` 2. **配置 npm 全局路径与镜像源** 修改全局模块安装路径和缓存路径,避免权限问题: ```bash npm config set prefix "D:\nodejs\node_global" npm config set cache "D:\nodejs\node_cache" ``` 使用国内镜像加速依赖下载: ```bash npm config set registry https://registry.npmmirror.com ``` #### 二、搭建 HTTP 服务器 1. **原生 Node.js 实现** 使用内置 `http` 模块创建基础服务器: ```javascript const http = require(&#39;http&#39;); const server = http.createServer((req, res) => { res.writeHead(200, {&#39;Content-Type&#39;: &#39;text/plain&#39;}); res.end(&#39;Hello Node.js!&#39;); }); server.listen(3000, () => console.log(&#39;Server running on port 3000&#39;)); ``` **最佳实践**:原生模块适合学习底层机制,但实际项目建议使用框架(如 Express、Koa)提高效率。 2. **使用 Express 框架** 安装 Express 并构建 RESTful API: ```bash npm install express ``` ```javascript const express = require(&#39;express&#39;); const app = express(); app.get(&#39;/&#39;, (req, res) => res.send(&#39;Express Server Running&#39;)); app.listen(3000, () => console.log(&#39;Server started on port 3000&#39;)); ``` #### 三、核心功能实现 1. **路由与中间件** 使用中间件处理请求日志和静态文件: ```javascript app.use(express.static(&#39;public&#39;)); // 托管静态文件 app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); }); ``` 2. **数据库集成** 以 MongoDB 为例,使用 `mongoose` 连接数据库: ```javascript const mongoose = require(&#39;mongoose&#39;); mongoose.connect(&#39;mongodb://localhost:27017/mydb&#39;) .then(() => console.log(&#39;MongoDB connected&#39;)) .catch(err => console.error(err)); ``` 3. **错误处理** 统一错误处理中间件: ```javascript app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send(&#39;Internal Server Error&#39;); }); ``` #### 四、部署与优化 1. **进程管理** 使用 `pm2` 实现生产环境进程守护: ```bash npm install pm2 -g pm2 start server.js ``` 2. **性能优化** - 启用集群模式:利用多核 CPU ```javascript const cluster = require(&#39;cluster&#39;); if (cluster.isMaster) { for (let i = 0; i < require(&#39;os&#39;).cpus().length; i++) cluster.fork(); } else { // 子进程启动服务器 } ``` - 使用缓存中间件(如 `redis`)减少数据库压力 3. **安全配置** - 设置 HTTP 头部安全策略(如 `helmet` 模块) - 输入验证防止 SQL 注入/XSS 攻击 #### 五、参考案例扩展 基于 Express 的博客系统可扩展用户认证、文件上传等功能[^2],例如: ```javascript // 用户登录接口示例 app.post(&#39;/login&#39;, (req, res) => { const { username, password } = req.body; // 验证逻辑 + JWT 令牌返回 }); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值