Azure App Service部署Node.js应用:mssql连接SQL Server失败排查
问题:Node.js Express部署到Azure App Service后无法连接SQL Server 2016
本地及Apache服务器上应用可正常连接SQL Server 2016执行查询,搭配React客户端运行无异常。但部署到Azure App Service后:
- 非数据库查询路由可正常响应
- 日志流显示SQL Server连接超时:
ConnectionError: Failed to connect to XX.XXX.XX.XX:XXXX in 15000ms - 请求
/getItems时sql.pool为undefined
已确认SQL Server配置管理器中TCP/IP启用、服务设为自动启动,调整mssql配置无效,需解决Azure环境下的适配问题。
相关代码:
mssql.js
var sql = require("mssql"); require("dotenv").config(); const config = { user: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, server: process.env.DB_SERVER, port: JSON.parse(process.env.DB_PORT), database: process.env.DB_NAME, requestTimeout: 3600000, //an hour options: { trustServerCertificate: true, encrypt: true, }, debug: true, parseJSON: true, }; let pool; const initConnect = async () => { try { const pool = await sql.connect(config); return pool; } catch (err) { console.log(err.message); console.log("Error connecting to DB...", err); sql.close(); } }; const initMod = async () => { this.pool = await initConnect(); console.log("open connecion..."); }; initMod(); module.exports.pool = pool; module.exports.SQLInst = sql;
server.js
const express = require("express"); const bodyParser = require("body-parser"); const cors = require("cors"); const app = express(); const sql = require("./mssql"); const port = process.env.PORT || 3000; app.use(cors()); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.get("/", async (req, res) => { res.send("Welcome to test server!"); }); // Getting items from db app.get("/getItems", async (req, res) => { console.log(`Getting items...`); try { const data = await sql.pool.request().query("SELECT * FROM PartsPics"); const items = await data.recordset; res.status(200).send(items); } catch (e) { console.log("Error occured: ", e); res.status(500).send(e); } });
解决方案
1. 修复连接池初始化的作用域问题
当前代码中连接池的赋值存在作用域错误,导致导出的pool始终是初始的undefined。修改mssql.js如下:
var sql = require("mssql"); require("dotenv").config(); const config = { user: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, server: process.env.DB_SERVER, port: parseInt(process.env.DB_PORT), // 替换JSON.parse,避免字符串转数字的潜在问题 database: process.env.DB_NAME, requestTimeout: 3600000, options: { trustServerCertificate: true, encrypt: true, }, debug: true, parseJSON: true, }; let pool; const initConnect = async () => { try { // 直接赋值给模块级变量,避免重新声明覆盖 pool = await sql.connect(config); console.log("数据库连接成功"); return pool; } catch (err) { console.error("连接数据库失败:", err.message); console.error(err); // 连接失败后终止进程,避免服务在无连接状态下运行 process.exit(1); } }; // 立即执行连接初始化 initConnect(); // 用getter导出pool,确保获取到最新的初始化后实例 module.exports = { get pool() { return pool; }, SQLInst: sql };
2. 配置Azure与SQL Server的网络权限
- 如果使用的是Azure SQL Server:在防火墙设置中勾选允许Azure服务和资源访问此服务器,确保App Service能穿透防火墙
- 如果是本地SQL Server:
- 开放SQL Server对应端口(默认1433)的公网访问权限
- 检查本地网络提供商是否拦截了该端口
- 在Azure App Service的「配置-应用程序设置」中,确认所有数据库环境变量(
DB_USERNAME、DB_PASSWORD等)填写正确,无拼写或格式错误
3. 优化路由中连接池的使用逻辑
添加连接池就绪检查,避免在初始化完成前调用:
// 修改server.js中的/getItems路由 app.get("/getItems", async (req, res) => { console.log("开始获取数据..."); try { let pool = sql.pool; // 等待连接池初始化完成 if (!pool) { await new Promise(resolve => setTimeout(resolve, 1000)); pool = sql.pool; if (!pool) { return res.status(503).send("数据库连接未就绪,请稍后重试"); } } const data = await pool.request().query("SELECT * FROM PartsPics"); res.status(200).send(data.recordset); } catch (e) { console.error("获取数据出错:", e); res.status(500).send(e.message || "服务器内部错误"); } });
4. 调整连接配置兼容性
- 根据SQL Server部署环境调整
encrypt选项:本地SQL Server可尝试设置encrypt: false,Azure SQL Server保持encrypt: true - 确保
trustServerCertificate的设置与SQL Server证书配置匹配
内容的提问来源于stack exchange,提问作者oz moyal
相关产品推荐
相关产品推荐

