服务器主要处理的是 I/O(输入/输出) 请求
简介
基础模块
1、fs:File System,操作文件的 (常用)
2、path:操作路径的(不常用)
3、http:创建 http 服务器的,处理客户端请求 (常用)
4、net:底层网络通信模块(不常用)
5、process:进程管理 (常用)
V8 核心:事件循环、内存原理
Node 版本管理工具:nvm(必装的)
使用 http 起一个简单的 server:
- 创建文件夹
mkdir base && cd base && touch index.js && pnpm init
- 写入代码
// 引入 http 模块
const http = require("http");
// 创建一个 HTTP 服务器
const server = http.createServer((req, resp) => {
// 从请求对象中获取请求方法和请求 URL
const { method, url } = req;
// 设置响应头的 Content-Type 为 "text/html; charset=utf-8"
resp.setHeader("Content-Type", "text/html; charset=utf-8");
// 设置响应状态码为 200
resp.statusCode = 200;
// 发送响应内容,并结束响应
resp.end(
`<h1>hello,我是使用 http 创建的服务器,你是一个:${method} | ${url}</h1>`
);
});
// 从环境变量中获取端口号,如果没有则使用默认值 3001
const port = process.env.PROT || 3001;
// 启动服务器并监听指定端口
server.listen(port, () => {
// 打印服务器运行信息
console.log(`HTTP Server running on http://localhost:${port}`);
});
- 运行
node index.js
ornodemon index.js
nodemon
:一个 Node.js 开发工具,用于监视项目文件的变化,并在检测到变化时自动重启 Node.js 应用。
安装npm install -g nodemon
字符集
字符集:它定义了一组符号和编码规则,用于表示文本中的字符。
写一个读取文件的 js
- 创建文件:fs.js
touch fs.js
- 写入代码:
const fs = require("fs");
const path = require("path");
function getFile(filePath) {
try {
const fileContent = fs.readFileSync(path.resolve(__dirname, filePath));
const fileContentUtf8 = Buffer.from(fileContent).toString("utf8");
console.log("[ fileContent ] >", fileContent);
console.log("[ fileContentUtf8 ] >", fileContentUtf8);
} catch (error) {
console.log("[ getFile error ] >", error);
}
}
getFile("./package.json");
- 运行
node fs.js
readFileSync
默认读取到的文件会以Buffer
展示
Buffer
是 nodejs 中处理二进制数据的类,可将其转为不同字符编码(utf8/base64/ascii 等)的数据。Buffer.from('原内容').toString('编码')
Buffer 类
const Buffer = require('buffer')
Buffer.alloc(size[, fill[, encoding]]) // 指定容量创建空 Buffer(已初始化的)
Buffer.allocUnsafe(size[, fill, ]) // 指定容量创建空 Buffer(未初始化的),所以不安全
Buffer.from(str[, encoding]) // 从已有数据创建 Buffer
Base64
base64 组成:小写 a-z,大写 A-Z,数字 0-9,符号 “+”“/”共 64 个字符(额外还有个“=”符号)
将字符串(图片等)转为二进制序列(010101...),然后每 6 个为一组进行分组,不足 6 个的低位补 0。分完组后,每6 个组成新的字节,高位补00
,构成新的二进制序列,然后转为十进制,最后在 base64 索引表中找该十进制对应的字符
-
一个字节 = 8比特(bit),是由 8 个二进制位组成的,其范围是从二进制的00000000到11111111,转换为十进制就是0到255。
-
ASCII字符集中的英文字符及其他一些符号可以用一个字节表示,因为ASCII字符集总共只有128个字符(0到127),小于一个字节所能表示的最大范围。
-
对于中文字符,以及其他许多非ASCII字符,由于字符数量众多,通常需要使用两个字节来表示。
Process
提供当前 nodejs 进程相关的信息
process.env
当前用户环境信息
{
USER: 'hzq',
PATH: '/Users/hzq/xxxx',
PWD: '/Users/hzq/code/mianshi/7-1 nodejs 基础/nodejs-base',
HOME: '/Users/hzq',
_: '/Users/hzq/.nvm/versions/node/v16.14.0/bin/node'
// .....
}
process.cwd()
返回当前 nodejs 进程的工作目录
process.cwd() // /Users/hzq/code/7-1 nodejs 基础/nodejs-base
Crypto
在 Node.js 中,crypto
模块是内置的加密和解密库,提供了丰富的密码学相关的功能,包括哈希(Hashing)、消息认证码(MACs)、加密(Encryption)、解密(Decryption)、签名(Signing)和验证(Verification)等。以下是 crypto
模块中一些常见用法:
- 哈希(Hashes) :
const crypto = require('crypto');
const hash = crypto.createHash('sha256');
hash.update('some data to hash');
const digest = hash.digest('hex'); // 得到哈希摘要
- HMAC(哈希消息认证码) :
const hmac = crypto.createHmac('sha256', 'secret key');
hmac.update('message');
const hmacDigest = hmac.digest('hex');
- 对称加密(Symmetric Encryption) :
const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32); // 生成一个密钥
const iv = crypto.randomBytes(16); // 生成一个初始化向量(IV)
// 加密
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update('message', 'utf8', 'hex');
encrypted += cipher.final('hex');
// 解密
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
- 非对称加密(Asymmetric Encryption) :
const { generateKeyPairSync } = require('crypto');
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
});
// 加密
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from('message'));
// 解密
const decrypted = crypto.privateDecrypt(privateKey, encrypted);
- 签名(Signatures) :
const sign = crypto.createSign('SHA256');
sign.write('some data to sign');
sign.end();
const signature = sign.sign(privateKey, 'hex');
// 验证签名
const verify = crypto.createVerify('SHA256');
verify.write('some data to sign');
verify.end();
const isVerified = verify.verify(publicKey, signature, 'hex');
Http1.x VS Http 2
1、传输格式:1.x 使用文本格式;2 使用二进制格式(更高效)
2、多路复用:1.x 一次 TCP 连接只能发送一次请求;2 可一次 TCP 连接发送多次请求(耗时更少),
3、服务器推送:1.x 不支持服务器主动推送内容给客户端;2 就支持
V8
事件循环
nodejs 端的事件循环跟浏览器端的有点不一样。
浏览器端的事件循环主要服务于UI 渲染和网络请求
nodejs 端的事件循环主要服务于 IO 处理、定时器、异步任务等
node 的宏任务、微任务
常见宏任务:I/O callbacks、timer(settimeout、setInterval)、setImmediate
常见微任务:Promise 回调、process.nextTick 等
循环流程:
1、入口进入,作为第一个宏任务被执行
2、执行同步代码直到结束
3、清空微任务列表,若有新的微任务产生,则继续清空
4、检查是否进行 I/O Poll 阶段
5、执行下一个宏任务
内存管理
在服务端内存是比较珍贵的,nodejs 限制了 64 位机器最大为 1.4 GB,限制了 32 位机器最大为 0.7GB
管理过程:
采用 GC:Garbage Collection - 垃圾回收 来管理内存
1、标记:通过遍历GC Root
来标记活动或非活动对象,遍历到的就是活动的,不能遍历到的就是非活动的
2、清除:将非活动对象清除
内存划分
堆:复杂类型;栈:基本类型;
分代垃圾回收
针对“堆”,划分了两个区域:新生代、老生代
新生代(存放短、容量小):
新创建的先放这里面。
采用 Scavenge 算法,又分为 From、To 区域
新创建先放 From 区域,当 From 区域满时,进行垃圾回收(标记清除) ,然后把存活的复制到 To 区域,之后翻转一下,即 From 变为 To,To 变成 From,之后重复直到两次垃圾回收(标记清除) 后还存在的就移动到老生代中
老生代(存放久,容量大):
多次在新生代里面存活的就晋升为老生代。
垃圾回收采用的是标记-清除或标记-整理
标记-整理: 基于标记清除,然后再整理了下内存碎片