1. 问题现象
后台博客信息页面请求以下接口时返回 500:
GET /api/xxxx/xxxx-setting/settings?key=xxxx-blog
返回错误:
{
"code": 500,
"data": null,
"message": "获取配置信息失败:connect ECONNREFUSED xxx.xxx.x.xxx:xxxx"
}
其他后台接口可以正常使用,因此问题表现为只有博客配置接口异常。
2. 根因分析
2.1 不是前端请求参数问题
管理端使用 GET 请求,并通过 key=xxxx-blog 获取博客配置,接口路径和请求方式正确。
服务端对应路由为:
server/routers/xxxx-setting.js
2.2 原接口在读取数据前执行了表结构迁移
原来的 GET 接口在查询 xxxx_setting 前,会先执行:
SHOW COLUMNS FROM xxxx_setting- 检查
xxxx_profile_tags - 检查 7 个 Hero 配置字段
- 字段不存在时执行
ALTER TABLE
因此该接口不是单纯读取数据,而是包含了数据库结构检查和修改操作。
2.3 数据库短暂拒绝连接会导致 Promise 永久失败
原代码使用模块级 Promise 缓存初始化结果。如果第一次请求恰好发生在 MySQL 重启期间,数据库连接会返回:
ECONNREFUSED xxx.xxx.x.xxx:xxxx
由于 rejected Promise 被继续缓存,后续请求仍然复用这个失败结果。即使 MySQL 已经恢复,接口也会持续报错,直到 Node 服务重启。
这解释了为什么其他接口正常,而 xxxx-setting 接口持续异常:问题出在该接口独有的初始化逻辑。
2.4 MySQL 日志说明
MySQL 日志显示:
Server hostname (bind-address): '*'; port: xxxx
ready for connections
说明 MySQL 已监听 xxxx,并且已经恢复连接。
以下日志暂时不是本次接口报错的直接原因:
- TLSv1/TLSv1.1 已废弃:安全协议警告
- CA certificate self signed:自签名证书警告
- Failed to start slave threads:未使用主从复制时通常无影响
- IP address could not be resolved:客户端 IP 反向 DNS 解析警告
3. 已实施的代码修复
3.1 抽离博客配置表结构初始化
新增:
server/modules/xxxx-setting/index.js
表结构初始化从路由文件中抽离,并由服务启动阶段执行。
3.2 GET 接口恢复为纯读取
GET 接口不再执行 SHOW COLUMNS 或 ALTER TABLE,只负责读取:
SELECT * FROM xxxx_setting WHERE xxxx_key = ?
这样可以避免用户访问页面时触发表结构变更,也避免单个配置页面被数据库迁移逻辑影响。
3.3 初始化失败后允许重试
表结构初始化失败时会清空缓存的 Promise:
schemaPromise = undefined;
数据库恢复后,服务启动或后续保存配置时可以重新尝试初始化,不会永久保留失败状态。
同时支持多个服务进程并发启动时的字段重复创建场景,ER_DUP_FIELDNAME 会被视为已完成。
3.4 服务启动时执行初始化
在:
server/app.js
中调用博客配置表结构初始化,并记录失败日志:
[BlogSetting] 初始化失败: ...
3.5 增加接口错误日志
GET 和 POST 失败时会记录配置 key、MySQL 错误码和错误信息。日志不记录数据库密码、Token 等敏感信息。
3.6 增加数据库端口配置支持
在:
server/mysql/db.js
中显式读取:
DB_PORT=xxxx
未配置时默认使用 xxxx。
4. 宝塔部署步骤
4.1 检查 MySQL
在宝塔终端执行:
systemctl status mysql
ss -lntp | grep xxxx
确认 MySQL 正常运行并监听 xxxx。
4.2 检查服务端环境变量
如果博客服务和 MySQL 在同一台服务器,建议:
DB_HOST=127.0.0.1
DB_PORT=xxxx
DB_NAME=xxxx-xxxx
DB_USER 和 DB_PASSWORD 继续使用服务器现有配置,不要提交到 Git 或文档中。
如果 MySQL 在独立服务器,需要确认数据库服务器防火墙和云安全组只允许博客服务服务器的 IP 访问 xxxx。
4.3 更新代码并重启 Node 服务
如果使用 PM2:
pm2 list
pm2 restart 实际服务名称 --update-env
如果使用宝塔 Node 项目管理,则在对应项目中执行重启。
重启是必要的,因为旧版本可能已经缓存了失败的 Promise。
4.4 验证接口
重新访问:
https://xxxx.xxxx.top/api/xxxx/xxxx-setting/settings?key=xxxx-blog
预期返回 code: 200,实际 data 内容以数据库中的 xxxx-blog 配置为准。
5. 本地验证结果
已完成以下静态检查:
node --check app.js
node --check mysql/db.js
node --check modules/xxxx-setting/index.js
node --check routers/xxxx-setting.js
git diff --check
检查通过。
本地没有直接启动服务连接生产数据库,避免对生产数据库执行结构初始化或修改。
6. 后续建议
当前修复已经解决接口因短暂数据库故障持续失败的问题。后续可以继续将数据库字段变更统一迁移到独立 migration 文件中,由部署流程显式执行,从根本上避免业务接口携带 DDL 操作。
private note
交流
文章暂不开放公开评论。如果你有想法、问题或建议,欢迎私下联系站长。
联系站长 →