重大技术栈转变

关于目前在学企业级技术栈SpringBoot 生态向 Node.js生态转换的决定

为了统一所学技术栈,终结前后端语言割裂的现象,践行全端(前后、移动、PC)

也便于日后JavaScript向TypeScript转变

Node.js

Express 是 Node.js的一个库,提供了一些扩展:

Express对 Node.js 的 HTTP 模块进行了封装,提供了更加简洁和灵活的 API 来构建 Web 应用和 API。

功能对比

特性Node.js 原生Express
路由处理需手动解析 URL 和方法提供简洁的路由 API
中间件支持需自行实现内置中间件机制
请求处理需手动解析请求体提供 body-parser 等工具
响应处理需手动设置响应头简化的响应方法
静态文件服务需手动实现一行代码搞定
错误处理需自行处理统一错误处理机制

现代 Node.js 技术栈

1. 框架生态

  • Express: 最流行的轻量级框架
  • Koa: Express 的下一代框架
  • Fastify: 高性能 Web 框架
  • NestJS: 受 Angular 和Spring 启发的企业级框架
  • Hapi: 配置驱动的框架

2. 适用实时应用

Node.js 的事件驱动和非阻塞 I/O 特性使其非常适合实时应用

事件驱动:事件驱动是一种编程范式,其中程序的执行流程由外部事件来决定;具体而言是通过:监听、回调来实现程序执行

补充:事件驱动在I/O操作中也可以理解成一种异步的I/O操作。

非阻塞I/O:发起I/O后并不阻塞,而是继续执行余下代码;I/O完成后再通过回调函数进行操作。

困惑解答

问:Node.js 被称为单线程却具有高并发处理能力?

答:

Node.js 确实是单线程的,但这个"单线程"指的是 JavaScript 执行线程 是单线程的,而不是整个运行时环境是单线程的。

比如,读取文件时,读取文件的那段代码是在主线程执行的,实际读取文件的操作是在其它线程完成的,这往往涉及到线程池和系统线程。下面是一个例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const fs = require('fs');
const crypto = require('crypto');

// 这些操作实际上使用了底层线程池
fs.readFile('file.txt', (err, data) => {
  // 回调在主线程执行,但实际的文件读取在其他线程完成
  console.log('文件读取完成');
});

crypto.pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, key) => {
  // 回调在主线程执行,但实际的加密计算在其他线程完成
  console.log('加密完成');
});

console.log('所有异步操作已发起');

另一个视角是Node.js事件驱动、非阻塞I/O的特点,这使它能够接收、处理多个并发连接,这是因为每个I/O都不阻塞,并且I/O完成后还可通过回调接着处理。    

总结

Node.js 的"单线程"和"高并发"并不矛盾:

  1. 单线程:指的是 JavaScript 代码执行在单个线程中,避免了多线程的复杂性
  2. 高并发:指的是能够同时处理大量 I/O 操作,通过非阻塞 I/O 和事件驱动实现

Node.js 通过以下机制实现高并发:

  • 非阻塞 I/O:I/O 操作不阻塞主线程
  • 事件驱动:通过事件循环处理完成的 I/O 操作
  • 线程池:对于某些操作使用底层线程池
  • 系统级异步 I/O:利用操作系统提供的异步 I/O 能力

这就是为什么 Node.js 特别适合 I/O 密集型应用(如 Web 服务器、API 服务)的原因。

问:Node.js既然有了多线程模型,并且还优于java,那么实际web应用开发中Node.js生态需不需要引入线程池,为什么

答:具体依照任务而定,如连接MySQL时需要引入数据库连接池,理由是:限制并发数据库查询数量,避免连接耗尽,数据库(如 MySQL)可能无法承受太多连接

总结

  • Node.js 不需要像 Java 那样广泛使用线程池,因为:

    1. 事件循环模型已优化高并发 I/O。

    2. Worker Threads 提供了更轻量的 CPU 并行方案。

  • 仅在以下情况考虑线程池

    • 长时间阻塞事件循环的 CPU 任务。

    • 需要精细控制并发度的场景(如数据库连接池)。

  • 替代方案

    • 用 cluster 扩展进程。

    • 用任务队列(如 bull)解耦耗时任务。

最终建议

  • 默认用事件循环(I/O 密集型)。

  • CPU 密集型任务 → Worker Threads 或专用微服务(如用 Go/Java 实现)

  • 线程池仅在特定场景下使用

问:和传统服务端技术的区别?

答:传统技术,如Apache + PHP 通过对多个请求创建多个线程,处理这些请求的始终,而Node通过一个主线程处理所有请求的始终,而中间过程的处理则交给其它线程。

传统服务端技术:

Node

开动啦

初始化后端项目

1. 安装依赖说明

(1) 生产依赖(后端必备)

npm install express cors dotenv

  • express: Node.js 后端框架

  • cors: 处理跨域请求(开发时前端访问后端 API 用)

  • dotenv: 加载 .env 环境变量

(2) 开发依赖(热更新调试)

npm install -D nodemon

  • nodemon: 监听文件变化自动重启 Node 服务(开发专用)