NodeJS+Express服务器根路由请求无响应问题排查求助
根路由GET请求无响应的排查与解决
问题描述
使用Node.js + Express搭建服务器并集成Socket.io后,向根路由http://localhost:3000/发送GET请求时无响应,请求一直处于等待状态。相关代码如下:
const express = require("express"); const axios = require("axios"); const cors = require('cors'); const app = express(); const { createClient } = require('redis'); const http = require('http'); const server = http.createServer(app); const { Server } = require("socket.io"); const db = require('./helper/db') const io = new Server(server, { cors: { origin: "*" } }); app.use(express.json()); app.use(cors); app.use(express.urlencoded({ extended: true })) app.get('/', (req,res) => { res.send('Backend NodeJS SO1 Proyecto2'); }); var claves = []; var total_registros = 0; var semestre_cursos = []; async function consultarRedis() { const client = await createClient({ url: 'redis://172.17.0.3:6379' }) .on('error', err => console.log('Redis Client Error', err)) .connect(); total_registros = await client.get('total_registros'); console.log(total_registros) } async function consultarRedisSemestre() { semestre_cursos = []; const client = await createClient({ url: 'redis://172.17.0.3:6379' }) .on('error', err => console.log('Redis Client Error', err)) .connect(); var temp = 0; for(var clave of claves){ try{ temp = await client.hGet('cursos_semestres', clave) semestre_cursos.push(parseInt(temp)) }catch(error){ console.error(`Error al consultar Redis para la clave "${clave}":`, error) } } } io.on('connection', (socket) => { console.log('Cliente conectado'); const interval = setInterval(() => { consultarRedis(); socket.emit('total_registros', total_registros); }, 1000); // Manejar desconexiones de socket.io socket.on('disconnect', () => { clearInterval(interval); console.log('Cliente desconectado'); }); socket.on('selectedSemester', (semestreSeleccionado) => { claves = []; claves.push(`SO1:${semestreSeleccionado}`) claves.push(`BD1:${semestreSeleccionado}`) claves.push(`LFP:${semestreSeleccionado}`) claves.push(`SA:${semestreSeleccionado}`) claves.push(`AYD1:${semestreSeleccionado}`) const interval = setInterval(async () => { temp = await consultarRedisSemestre(); socket.emit('valores_semestre', semestre_cursos); }, 1000); }); }); server.listen(3000, () => { console.log("Server on port 3000"); });
问题排查与解决
1. CORS中间件调用错误
这是导致请求挂起的直接原因:
原代码中app.use(cors);没有调用cors()函数,而是直接将函数本身作为中间件传入。Express中间件需要是一个可执行的处理函数,未调用的cors只是函数引用,会直接阻塞请求处理流程。
修复方式:
将app.use(cors);修改为:
app.use(cors());
2. Redis客户端未关闭连接
每次调用consultarRedis和consultarRedisSemestre都会创建新的Redis客户端,但没有主动关闭连接,长期运行会导致Redis连接池耗尽,阻塞Node.js事件循环,间接影响请求处理效率。
修复方式:
复用Redis客户端,避免重复创建连接:
// 全局复用Redis客户端 let redisClient; async function initRedisClient() { redisClient = createClient({ url: 'redis://172.17.0.3:6379' }) .on('error', err => console.log('Redis Client Error', err)); await redisClient.connect(); } // 初始化客户端 initRedisClient(); async function consultarRedis() { if (!redisClient) return; try { total_registros = await redisClient.get('total_registros'); console.log(total_registros); } catch (err) { console.error('Redis查询错误:', err); } } async function consultarRedisSemestre() { semestre_cursos = []; if (!redisClient) return; for(var clave of claves){ try{ const temp = await redisClient.hGet('cursos_semestres', clave); semestre_cursos.push(parseInt(temp) || 0); // 处理null返回值 }catch(error){ console.error(`Error al consultar Redis para la clave "${clave}":`, error); } } }
3. Socket.io定时器内存泄漏
在selectedSemester事件中创建的定时器没有在客户端断开连接时清理,会导致内存泄漏,长期运行会降低服务器稳定性。
修复方式:
将定时器存储在socket对象上,断开连接时统一清理:
socket.on('selectedSemester', (semestreSeleccionado) => { claves = [ `SO1:${semestreSeleccionado}`, `BD1:${semestreSeleccionado}`, `LFP:${semestreSeleccionado}`, `SA:${semestreSeleccionado}`, `AYD1:${semestreSeleccionado}` ]; // 清理之前的定时器(如果存在) if (socket.semesterInterval) { clearInterval(socket.semesterInterval); } socket.semesterInterval = setInterval(async () => { await consultarRedisSemestre(); socket.emit('valores_semestre', semestre_cursos); }, 1000); }); // 在disconnect事件中清理所有定时器 socket.on('disconnect', () => { clearInterval(interval); if (socket.semesterInterval) { clearInterval(socket.semesterInterval); } console.log('Cliente desconectado'); });
修改后的完整代码
const express = require("express"); const axios = require("axios"); const cors = require('cors'); const app = express(); const { createClient } = require('redis'); const http = require('http'); const server = http.createServer(app); const { Server } = require("socket.io"); const db = require('./helper/db') const io = new Server(server, { cors: { origin: "*" } }); // 修复CORS中间件调用 app.use(express.json()); app.use(cors()); app.use(express.urlencoded({ extended: true })) app.get('/', (req,res) => { res.send('Backend NodeJS SO1 Proyecto2'); }); var claves = []; var total_registros = 0; var semestre_cursos = []; // 全局Redis客户端 let redisClient; async function initRedisClient() { redisClient = createClient({ url: 'redis://172.17.0.3:6379' }) .on('error', err => console.log('Redis Client Error', err)); await redisClient.connect(); } initRedisClient(); async function consultarRedis() { if (!redisClient) return; try { total_registros = await redisClient.get('total_registros'); console.log(total_registros); } catch (err) { console.error('Redis查询total_registros错误:', err); } } async function consultarRedisSemestre() { semestre_cursos = []; if (!redisClient) return; for(var clave of claves){ try{ const temp = await redisClient.hGet('cursos_semestres', clave); semestre_cursos.push(parseInt(temp) || 0); }catch(error){ console.error(`Error al consultar Redis para la clave "${clave}":`, error); } } } io.on('connection', (socket) => { console.log('Cliente conectado'); const interval = setInterval(() => { consultarRedis(); socket.emit('total_registros', total_registros); }, 1000); socket.on('disconnect', () => { clearInterval(interval); // 清理学期定时器 if (socket.semesterInterval) { clearInterval(socket.semesterInterval); } console.log('Cliente desconectado'); }); socket.on('selectedSemester', (semestreSeleccionado) => { claves = [ `SO1:${semestreSeleccionado}`, `BD1:${semestreSeleccionado}`, `LFP:${semestreSeleccionado}`, `SA:${semestreSeleccionado}`, `AYD1:${semestreSeleccionado}` ]; // 清理之前的定时器 if (socket.semesterInterval) { clearInterval(socket.semesterInterval); } socket.semesterInterval = setInterval(async () => { await consultarRedisSemestre(); socket.emit('valores_semestre', semestre_cursos); }, 1000); }); }); server.listen(3000, () => { console.log("Server on port 3000"); });
内容的提问来源于stack exchange,提问作者Pedro Castro
相关产品推荐
相关产品推荐

