分布式微服务系统架构第122集:NestJS是一个用于构建高效、可扩展的服务器端应用程序的开发框架

加群联系作者vx:xiaoda0423

仓库地址:webvueblog.github.io/JavaPlusDoc...

1024bat.cn/

nestjs + typescript技术,ORM使用sequelize,数据库采用mysql。前端采用vue2全家桶,搭配SSR技术

安装

开始之前,你可以使用 [Nest CLI] 创建项目,也可以克隆一个 starter project(两者的结果是一样的)。

若要使用 Nest CLI 构建项目,请运行以下命令。这将创建一个新的项目目录,并使用核心的 Nest 文件和支撑模块填充该目录,从而为项目创建一个传统的基本结构。建议初学者使用 Nest CLI 创建新项目。

ruby 复制代码
$ npm i -g @nestjs/cli
$ nest new project-name

复制

其他安装方式

或者,使用 Git 安装采用 TypeScript 开发的 starter 项目:

shell 复制代码
$ git clone https://github.com/nestjs/typescript-starter.git project
$ cd project
$ npm install
$ npm run start

打开浏览器并导航到 [http://localhost:3000/] 地址。

若要安装基于 JavaScript 的 starter project,请在执行上面的命令时使用 javascript-starter.git

你还可以通过 npm (或 yarn)来安装的核心和支撑文件,从头开始手动创建一个新项目。当然,在这种情况下,你将自己担负起创建项目样板文件的工作。

css 复制代码
$ npm i --save @nestjs/core @nestjs/common rxjs reflect-metadata

这个错误信息说明你的 Node.js 项目在使用 Sequelize 连接 MySQL 数据库时发生了 连接超时(ETIMEDOUT) ,根本原因通常是网络层面的问题。我们可以逐步排查:


✅ 可能原因 & 解决方案:

1. 数据库地址无法访问

  • Sequelize 配置中设置的 host 是不是写错了?是内网 IP、localhost 还是公网域名?

  • 如果是远程数据库(例如云数据库),你当前的机器是否能 ping 通或 telnet 它?

    yaml 复制代码
    ping your-database-host
    telnet your-database-host 3306

2. MySQL 未启动或监听端口错误

  • 确认你的 MySQL 服务是否启动?
  • 监听的是默认端口 3306 吗?

3. 防火墙、网络安全组拦截

  • 服务器防火墙是否放行了 3306 端口?
  • 云服务提供商(如阿里云、腾讯云)需要你配置安全组允许你的 IP 访问 MySQL。

4. 数据库连接配置错误

请检查 Sequelize 的连接参数是否正确(尤其是 host、port、user、password、database):

arduino 复制代码
const sequelize = new Sequelize('database_name', 'username', 'password', {
  host: 'localhost', // 或者云数据库地址
  dialect: 'mysql',
  port: 3306,         // 确保端口对得上
  dialectOptions: {
    connectTimeout: 10000 // 延长连接超时时间,单位是毫秒
  }
});

5. 网络不稳定 / 连接超时设置太短

你可以尝试临时增加 connectTimeout 参数(单位毫秒):

css 复制代码
dialectOptions: {
  connectTimeout: 15000 // 默认 10000,试着调大
}

6. 本地开发网络有问题(如 VPN、代理)

如果你连的是远程数据库,但本地连不上,试着关闭 VPN、代理软件再试。


🔧 你可以先这么试试:

  1. 确认数据库服务是否在运行,并且监听在正确的地址和端口。
  2. 确认你的 Sequelize 配置是否写对。
  3. 在命令行用 telnetmysql -h 命令测试连接。
  4. 适当增加 connectTimeout 看是否是网络抖动。

你的数据库连接报错 ETIMEDOUT,而配置中 host 是 localhost,说明:

  • 你运行 Node.js 项目的这台电脑上没有跑起 MySQL 服务,或者服务挂了。
  • 或者 MySQL 没监听 127.0.0.1:3306(有可能只监听 socket 或者其他 IP)。

✅ 解决步骤建议:

✅ 1. 检查 MySQL 是否在本机启动

打开终端或 CMD:

css 复制代码
mysql -u root -p

如果提示无法连接,就说明 MySQL 没启动,使用以下命令尝试启动(根据你安装方式不同):

  • Windows:

    sql 复制代码
    net start mysql
  • macOS(使用 Homebrew):

    sql 复制代码
    brew services start mysql
  • Linux:

    sql 复制代码
    sudo systemctl start mysql

✅ 2. 确认 MySQL 监听了 127.0.0.1:3306

运行以下命令确认监听端口:

yaml 复制代码
netstat -an | findstr 3306    # Windows
lsof -i:3306                  # mac/Linux

如果没有看到 127.0.0.1:3306localhost:3306,说明 MySQL 配置问题。

你可以修改 my.cnfmy.ini 文件,确认配置中 bind-address = 127.0.0.1,然后重启 MySQL。


✅ 3. 确认数据库 mp_release_platform 存在

用客户端工具(如 Navicat、DBeaver)或者命令行连接:

css 复制代码
mysql -u root -p
SHOW DATABASES;

确认 mp_release_platform 是存在的。


✅ 4. Sequelize 初始化代码增加日志与超时配置

建议你初始化 Sequelize 时加点日志,方便排查连接过程:

yaml 复制代码
const Sequelize = require('sequelize');
const { mysql } = configure;

const sequelize = new Sequelize(
  mysql.database,
  mysql.user,
  mysql.password,
  {
    host: mysql.host,
    port: mysql.port,
    dialect: 'mysql',
    pool: {
      max: mysql.connectionLimit,
      min: 0,
      acquire: 30000,
      idle: 10000,
    },
    logging: console.log, // 打印 SQL 日志,便于调试
    dialectOptions: {
      connectTimeout: 10000, // 默认超时时间延长一点
    }
  }
);

🔧 补充建议

如果还是连不上,你可以试试把 host 从 localhost 改成 127.0.0.1

vbnet 复制代码
host: '127.0.0.1',

有些系统把 localhost 映射成 ::1(IPv6),而 MySQL 默认可能没监听 IPv6。

🔍 你的 MySQL 当前监听状态分析:

ini 复制代码
TCP    0.0.0.0:3306           0.0.0.0:0              LISTENING
TCP    [::]:3306              [::]:0                 LISTENING

这说明你的 MySQL:

  • ✅ 已经成功监听了所有 IPv4 和 IPv6 地址的 3306 端口
  • [::1]:3306 有连接,说明你本机某些程序(也许是 Navicat 或命令行)已经成功连接 MySQL。

✅ 结论:MySQL 是开启的,监听也没问题,但 Sequelize 用的 localhost 可能存在 IPv6 映射问题


🔧 解决建议

✅ 1. Sequelize 配置中将 host: 'localhost' 改为 127.0.0.1

资源文件read-image文件夹

项目组模块,系统管理模块,发布预览,发布体验版,发布任务记录,日志,小程序二维码,用户操作选项卡。

预览 分支, 启动页面,启动参数

发布体验版:分支,版本,自动生成,格式:年月日,今日发布次数

bash 复制代码
# 安装所需包
npm i
arduino 复制代码
# 运行项目
npm run start:dev
bash 复制代码
平台打包为docker镜像
make
# 通过docker服务编排同时生成并启动
docker-compose up
ini 复制代码
# 完整配置
mysql:
    restart: always
    image: mysql:5.6
    volumes:
        - ./data:/var/lib/mysql
    command: [
      '--character-set-server=utf8',
      '--collation-server=utf8_general_ci',
      --default-authentication-plugin=mysql_native_password,
    ]
    environment:
        - MYSQL_ROOT_PASSWORD=root
        - MYSQL_DATABASE=mp_release_platform
        - MYSQL_USER=mp
        - MYSQL_PASSWORD=mp2020
    ports:
        - "3306:3306"
bash 复制代码
# 单独启动mysql
docker-compose up -d mysql
# 查看mysql容器的id
docker ps
# 进入mysql容器
docker exec -it mysql容器id bash
# 连接mysql
mysql -ump -pmp2020
# 删除数据库
DROP DATABASE mp_release_platform
# 创建数据库并执行字符集
CREATE DATABASE mp_release_platform DEFAULT CHARSET utf8 COLLATE utf8_general_ci

npm install或yarn install只会安装dependencies,而不会安装devDependencies列表中的包,需要将安装命令改为npm install --dev或yarn install --production=false

排查k8s分配给docker容器的内存大小是否足够,若k8s分配给docker容器的内存足够大,依然报内存溢出,则可能是系统分配给node的内存不足(tips: 分配给node程序的内存64位系统下约为1.4GB,32位系统下约为0.7GB)

在小程序中,发起请求时会涉及到多个并发流程和事件循环,下面详细描述了从发起请求到结束请求的整个过程,以及超时、挂起和低端手机性能慢时可能会遇到的各种情况。

执行流程:从发起请求到结束请求

这个流程将详细说明从发起请求到请求结束时的整个过程,包括并发请求、超时、挂起、低端手机性能慢的影响,以及如何优化这些问题。

1. 发起请求

1.1 初始化请求

  • 请求函数 request(options) 被调用
  • 用户发起的请求可以是 GETPOSTPUT 等,其中会包含 URL、请求参数、配置项等。

1.2 构建请求头

  • token(如果存在)会加入请求头,用于鉴权。
  • cnrLabelfuLabel 等是请求所需的额外数据(如城市编码、标签等),这些信息从缓存中读取并附加到请求头中。

1.3 记录请求的开始时间

  • 请求发起前,记录请求的起始时间 (使用 Date.now()performance.now())。

1.4 发起请求

  • 使用 Taro.request() 发送 HTTP 请求。此时,网络请求进入 事件循环的宏任务队列
  • 请求的状态会在 successfail 回调中处理。

2. 等待响应

2.1 网络请求等待响应

  • 网络请求处于挂起状态,等待从服务器返回响应。在此期间,事件循环中的其他任务(如页面渲染、异步计算、定时器等)会被继续处理。
  • 如果有多个并发请求,它们会分别进入事件循环,并按照响应顺序依次处理。

2.2 超时设置

  • 如果请求在规定时间内(例如 timeout: 60s)未能得到响应,超时错误 会触发,并由 catch 处理。
  • 低端手机性能慢时,网络请求可能会由于手机的处理能力不足,导致响应慢或超时。

2.3 挂起(网络差)

  • 网络质量差高延迟 的情况下,尽管设备能够正常发起请求,但网络响应时间较长,导致请求处于 挂起状态
  • 此时,界面可能会冻结,用户无法交互,或者显示等待/加载中的状态。

3. 响应处理与数据解析

3.1 收到响应

  • 当服务器返回响应时,进入微任务队列 ,并会调用 success 回调处理。
  • 在响应到达时,记录 响应的结束时间 (通过 Date.now())。

3.2 状态码校验

  • 服务器的响应会根据状态码进行校验。

    • 如果状态码在 200 到 299 之间,视为成功,继续处理响应。
    • 如果响应状态码不在此范围,触发 错误处理 ,通过 Taro.showToast 提示用户错误。
  • 如果 response.code !== 0,表示业务层的错误,显示具体的错误信息并中止当前操作。

3.3 数据解析与处理

  • 解析响应体:

    • 如果请求处理正常,数据会返回给调用方。

3.4 网络请求错误处理

  • 如果在请求过程中发生错误(如超时、网络中断、服务器错误等),会进入 catch 中:

    • 记录错误信息。
    • 根据 超时重试策略(如指数退避),决定是否重试请求。
    • 显示相应的错误提示,提示用户网络不稳定或请求失败。

4. 并发请求与性能考虑

4.1 并发请求

  • 当多个请求同时发起时,它们会进入 事件循环,并按照顺序处理回调。

  • 由于事件循环中每次只能处理一个宏任务(即只有一个请求会被立即处理),可能会导致在低端设备上 请求积压

    • 优化方式:使用 请求并发限制,将大量并发请求拆分成多个批次,避免一次性发送大量请求。
    • 或者将请求合并,减少请求的数量。

4.2 低端手机性能慢

  • 低端手机CPU 和内存限制 会导致:

    • 页面渲染慢,尤其在处理大量请求或复杂数据时。
    • 请求响应慢,尤其在网络环境不佳时。
  • 优化方式

    • 骨架屏:避免白屏,展示一个占位图,提示用户正在加载。
    • 请求延迟加载:不需要立即加载的数据可以延迟加载,避免一开始加载大量数据。
    • 批量请求:避免一次性发起大量请求,优先选择合并请求或者分批请求。

4.3 网络差与请求挂起

  • 在网络状况不佳时,设备可能会一直处于等待响应的状态,直到超时。

  • 优化方式

    • 使用 超时重试机制,在网络条件恢复时进行重试,减少超时失败。
    • 请求取消:在页面销毁时,取消所有挂起的请求,避免无效的请求占用资源。

5. 超时与重试机制

5.1 超时

  • 设置合理的 超时参数,通常在低端设备上可以适当增加超时时间(例如 60s)。
  • 当请求超时时,通过 catch 进行处理,显示用户友好的错误提示。

5.2 重试机制

  • 当请求超时或出现暂时的网络问题时,自动重试机制 可以尝试重新发起请求。常用的重试策略是 指数退避,每次失败后重试时,间隔时间成倍增加:

    • 第一次重试:1s
    • 第二次重试:2s
    • 第三次重试:4s
    • 依此类推,避免请求过于频繁。

执行流程:

  1. 用户发起请求,进入宏任务队列。
  2. 网络请求挂起,等待服务器响应。
  3. 响应返回后,进入微任务队列处理回调。
  4. 状态码校验、数据解析与业务处理。
  5. 发生错误时,通过 catch 进行错误处理,必要时重试。
  6. 并发请求时,按照顺序依次执行,避免一次性处理大量请求。
  7. 在低端设备上,优化请求和渲染逻辑,避免掉帧和性能瓶颈。

关键点:

  • 超时与重试机制,适应低端手机的网络和性能问题。
  • 请求合并与并发限制,减少请求数量,提高性能。

1. 请求发起与事件循环

  • 用户调用 Taro.request() 时会创建一个异步请求。
  • 请求会被推入 事件循环(Event Loop) ,此时执行上下文并不等待网络请求完成,而是继续执行后续代码。
  • 在请求前,我们会先构建请求头(包括 tokenapp-type、 等信息),并将这些数据通过 请求头请求参数 传递。
  • 使用 Taro.request() 发送网络请求。此时,请求进入宏任务队列,会异步执行,并且不会阻塞后续代码的执行。

2.1 响应接收

  • 一旦服务器响应返回,回调函数(例如 .then().catch())会被加入到 微任务队列,并在当前宏任务完成后执行。
  • 请求的响应数据会被进一步处理(如状态码校验、数据解析等)。

2.2 响应超时与挂起

  • 如果网络请求在规定时间内(如 60s)没有收到响应,或因为网络不稳定导致请求挂起,超时错误会被触发并处理。
  • 网络请求会被终止,并且错误信息会通过 .catch() 捕获。

代码示例

javascript 复制代码
catch(e => {
  // 处理网络请求超时或错误
  console.error('请求失败', e);
  Taro.showToast({ title: "网络请求错误,请稍后重试", icon: "none" });
})

2.3 状态码与错误处理

  • 网络请求成功后,首先会校验响应的状态码,确保状态码在 200 到 299 之间。如果状态码不符合,则会弹出错误提示并记录错误日志。

2.5 性能优化:请求并发与合并

  • 在高并发请求场景下,避免一次性发起大量请求,可以通过 限制并发请求数合并请求 来优化性能。

3. 请求超时与重试机制

3.1 设置超时

  • 可以在请求中设置合理的 超时时间,例如 60s。在低端设备上,可以适当增加超时时间以适应网络差或设备性能差的情况。

代码示例

yaml 复制代码
timeout: 60 * 1000 // 设置超时时间为60秒

3.2 重试机制

  • 如果请求由于超时或偶发网络错误失败,可以通过 重试机制 来恢复请求。
  • 使用 指数退避 策略来增加每次失败后的重试间隔。

3.3 请求取消

  • 在低端设备或网络差的情况下,可以通过请求取消来避免无效的请求占用系统资源。
  • 使用 Taro 提供的 abort 方法来取消挂起的请求。

代码示例

ini 复制代码
const requestTask = Taro.request({ url, data });
requestTask.abort(); // 在需要时取消请求

4. 低端设备的性能优化

4.1 UI 渲染优化

  • 在低端设备上,频繁的页面渲染和状态更新会导致 UI 卡顿。优化建议:

    • 使用 骨架屏,提前显示占位图,避免页面空白。
    • 合并多个 setState() 操作,避免多次触发组件重渲染。

4.2 懒加载与分页加载

  • 对于数据量大的场景,可以使用 懒加载分页加载 来减少一次性加载的数据量。
  • 例如,在页面加载时,首先加载核心数据,其他数据通过滚动加载或触底加载来逐步获取。

小程序发起请求 ,请求被挂起或等待超过 100 秒 才返回数据。这类问题通常和网络环境、请求超时、服务器响应时间或客户端性能等多方面因素有关

1. 请求发起过程

1.1 用户发起请求

  • 小程序用户通过调用如 Taro.request() 等 API 发起网络请求。这个请求会进入 事件循环的宏任务队列,等待执行。
  • 请求包含了所需的 URL、参数、请求头等数据。

1.2 请求被发送到服务器

  • 请求会发送到后端服务器,服务器开始处理请求。此时,小程序进入 等待响应 状态。UI 可能显示加载框或骨架屏,提示用户正在加载数据。
  • 如果是一个耗时较长的操作(例如复杂计算、数据库查询等),服务器可能需要一些时间来生成响应。

1.3 小程序继续执行其他任务

  • 在网络请求发起后,小程序的 主线程 会继续执行 其他 JavaScript 代码(例如处理 UI 渲染、事件监听、定时任务等),而不会被阻塞。

2.2 网络不稳定或带宽限制

  • 服务器可能已经处理完请求并生成了响应,但由于 网络环境不佳 ,数据无法及时传输到客户端。这种情况下,数据传输过程中可能会遇到 网络瓶颈,导致请求延迟。

    • 移动网络 下,带宽的波动、信号不稳定、丢包等问题可能导致数据传输速率变慢。
    • 网络丢包或网络中断可能导致客户端无法接收到数据,需要重新连接。

2.3 请求未设置合理的超时时间

  • 如果请求的 超时时间 设置过长或没有设置,可能会导致 请求挂起,即使服务器已经响应,客户端仍然等待数据。

    • 如果没有设置 timeout 参数,或者设置时间过长(如 100s),客户端会一直等待直到请求完成或超时。
    • 客户端等待期间,UI 可能仍处于 等待状态,而实际请求已完成。

2.5 低端设备性能问题

  • 如果请求涉及大量数据处理,或者设备性能不足(例如 低端手机),可能会导致:

    • 请求队列积压:多个请求并发时,设备处理能力有限,导致请求无法及时得到处理。
    • UI 卡顿或掉帧:设备性能差时,UI 更新和事件响应可能变慢,导致数据加载过程中的界面冻结或延迟响应。

3.1 网络请求的超时和重试机制

  • 为了避免请求挂起,应该 设置合理的超时时间。例如,可以将超时时间设置为 30s 或 60s,超时后重新发起请求。
  • 可以实现 超时重试机制 ,当请求失败时,可以设置重试次数,并使用 指数退避策略(每次重试间隔增加)来避免频繁请求。

如果条件允许,可以使用 CDN边缘计算 加速服务器响应速度,并在服务器与客户端之间提供稳定的连接。

3.4 低端设备优化

  • 优化请求和渲染流程 ,避免同时发起大量请求。可以采用 懒加载分页加载 只加载必要的数据,减少页面渲染负担。
  • 在低端设备上,避免复杂的 UI 渲染 ,减少组件的更新次数。例如,可以使用 骨架屏加载动画,避免用户在数据加载时看到空白页面。

3.5 合理设置超时

  • 设置 合适的超时时间,避免请求长时间挂起。通常,可以将超时时间设置为 30s 或 60s。超时后可以通过重试机制重新发起请求。

代码示例

php 复制代码
Taro.request({
  url: 'https://example.com/api',
  timeout: 30000  // 设置超时为 30s
})

1. 请求发起过程

1.1 用户发起请求

  • 用户通过 Taro.request() 或类似的请求 API 发起网络请求。此时,网络请求被加入到 事件循环的宏任务队列,并且执行后续代码。

1.2 请求的超时设置

  • 在请求选项中,通常会设置 timeout 参数(例如 60 秒)。这是用来限制网络请求的最大等待时间。如果请求超时,会触发超时错误,停止请求并返回超时异常。

设置 timeout 参数后,理论上,当请求超过 30 秒(例如),就会自动中止请求并抛出超时异常。

超时机制的触发时机问题

超时机制实际上是 由底层 HTTP 客户端(如 XMLHttpRequestfetch)管理的,在请求过程中,如果响应时间超过了设定的超时限制,应该会抛出超时错误。然而,某些情况下,超时设置未能生效,可能是因为:

  • 网络延迟问题:当网络信号非常差时,尽管设定了超时,底层 HTTP 客户端可能并不会立即中断请求。相反,网络会持续等待数据传输完毕,即使请求超时,也可能会挂起一段时间。
  • 请求挂起:如果服务器响应非常慢或挂起,可能会导致请求持续等待直到超时发生,但在等待过程中并没有主动中止。这是因为超时机制依赖于底层网络层的反馈,而底层可能并没有对请求进行适当的超时控制。
  • 低级网络错误 :某些类型的 网络错误 (例如 DNS 查找失败、网络断开等)可能不会触发 timeout 错误,而是导致请求卡住。此时,浏览器或小程序的底层并未检测到异常状态,因此并没有立即中止请求。
  • 小程序特有问题 :在某些 小程序框架 中,可能存在特定的 bug实现问题,使得超时参数未能正常触发。

服务器延迟或处理时间长

  • 请求可能需要较长时间来处理,尤其是涉及 复杂计算数据库查询外部 API 调用 时。如果服务器响应时间过长,超时机制可能并未中断正在处理的请求,从而导致请求一直挂起直到超时。

客户端不具备主动中断请求的机制

  • 即使设置了超时,某些 请求的底层实现 可能并不具备 主动中断 请求的机制。在请求等待过程中,超时只会触发一个警告,而不是主动终止该请求。

增加超时时间与重试机制

  • 优化超时设置:在超时设置较短的情况下,可以适当增加超时时间,避免短时间内的请求失败。
  • 重试机制:如果请求超时,可以通过重试机制来恢复请求,避免单次请求的失败。

代码示例

php 复制代码
const requestTask = Taro.request({
  url: 'https://example.com/api',
  timeout: 30000
});

requestTask.abort();  // 超时后主动中止请求

优化低端设备上的请求性能

  • 低端设备 下,请求响应慢 可能是由于设备性能差,或者 UI 渲染过于频繁导致的性能瓶颈。可以考虑 分批加载数据使用骨架屏 来优化用户体验。
相关推荐
万少20 分钟前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL25 分钟前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl0241 分钟前
java web5(黑马)
java·开发语言·前端
Amy.Wang42 分钟前
前端如何实现电子签名
前端·javascript·html5
今天又在摸鱼1 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
君爱学习1 小时前
RocketMQ延迟消息是如何实现的?
后端
蓝婷儿1 小时前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端
百锦再1 小时前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref
jingling5551 小时前
面试版-前端开发核心知识
开发语言·前端·javascript·vue.js·面试·前端框架
拾光拾趣录1 小时前
CSS 深入解析:提升网页样式技巧与常见问题解决方案
前端·css