Nodejs express使用res.json()客户端出现request time out的问题。
Nodejs express使用res.json()客户端出现request time out的问题。
使用express,在最后调用 return res.json(200,user);最为用户登陆成功的应答,频繁出现iOS端第一次登陆会出现request time out错误,再登陆一次,迅速成功的情况。而node端的log里面显示响应已经成功了。
使用Charles观察网络请求,发现客户端登陆请求发出后,/login请求的状态为:
received response header,waiting for response body
而不是‘完成’状态,当客户端请求超时后,再次发出/login请求,将100%成功的应答。 iOS客户端使用AFNetworking node的代码如下:
exports.doLogin = function(req,res) {
if(req.body.phone === undefined || req.body.email === undefined){
return res.json(400,{'err':'Please input your username and password'});
}
var md5 = crypto.createHash('md5');
var password = md5.update(req.body.password).digest('base64');
User.getByLogin(req.body, function(err,user){
if(!user){
return res.json(401,{'err':'user does not exist'});
}
if(user.user.userInfo.password != password){
return res.json(402,{'err':'password does not match'});
}
req.session.cookie.originalMaxAge = settings.maxAge;
req.session.user = user;
return res.json(200,user);
});
};
Node.js Express 使用 res.json()
客户端出现 Request Timeout 的问题
问题描述
在使用 Express 框架处理用户登录请求时,客户端(iOS 端)在第一次登录时频繁出现 Request Timeout
错误,而在第二次登录时会立即成功。然而,从 Node.js 端的日志来看,响应已经成功发送。
分析
通过 Charles 抓包工具观察到,客户端发送 /login
请求后,服务器返回了响应头但没有返回响应体。这表明请求可能在某些情况下没有完全处理完毕或响应被阻塞了。
可能的原因
- 异步操作未完成:在处理用户登录逻辑时,可能存在某些异步操作(如数据库查询)尚未完成,导致响应未能及时返回。
- 资源竞争或阻塞:在高并发场景下,可能因为资源竞争或阻塞导致响应延迟。
- 网络延迟或客户端超时设置:客户端的超时时间设置过短,导致在正常响应之前超时。
示例代码与改进措施
以下是改进后的示例代码,确保所有异步操作都已完成后再发送响应。
const express = require('express');
const crypto = require('crypto');
const User = require('./models/User'); // 假设你有一个 User 模型
const settings = require('./settings'); // 假设你有一个配置文件
const app = express();
app.use(express.json());
exports.doLogin = async function (req, res) {
try {
if (!req.body.phone || !req.body.email) {
return res.status(400).json({ err: 'Please input your username and password' });
}
const md5 = crypto.createHash('md5');
const password = md5.update(req.body.password).digest('base64');
const user = await User.getByLogin(req.body);
if (!user) {
return res.status(401).json({ err: 'User does not exist' });
}
if (user.user.userInfo.password !== password) {
return res.status(402).json({ err: 'Password does not match' });
}
req.session.cookie.originalMaxAge = settings.maxAge;
req.session.user = user;
return res.status(200).json(user);
} catch (error) {
console.error(error);
return res.status(500).json({ err: 'Internal server error' });
}
};
// 路由定义
app.post('/login', exports.doLogin);
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
解释
- 异步处理:使用
async/await
语法确保所有异步操作(如数据库查询)都已完成后再发送响应。 - 错误处理:添加
try/catch
块来捕获并处理可能出现的异常,避免服务器崩溃。 - 状态码:使用
res.status(code).json(data)
方法来明确返回状态码和数据。
通过这些改进,可以有效减少因异步操作未完成或资源竞争导致的响应延迟问题,从而解决客户端的 Request Timeout
问题。
密码没加盐啊貌似。
试着不用 ios 客户端,换个方式测测看 node server 导致有没有问题嘛。既然 node 的 log 里显示成功的话,我觉得应该去调下客户端。
恩,没加盐。密码传输也为明文,通信协议也没有。。。 关键是 waiting for response body 这个连接状态很诡异啊。
根据你的描述,问题可能出现在以下几个方面:
-
数据库查询慢:
User.getByLogin
这个方法可能因为数据库查询效率低导致响应时间过长。可以通过优化查询语句或者添加索引来提高查询效率。 -
服务器配置:确保服务器能够处理高并发请求,例如增加 worker 线程数量或者优化 Node.js 的 event loop 配置。
-
iOS 客户端超时设置:检查 iOS 客户端的网络请求超时设置,确保它足够长以覆盖正常的响应时间。
-
日志分析:查看 Node.js 日志,确认
User.getByLogin
方法的执行时间和响应时间是否正常。
示例代码优化:
const crypto = require('crypto');
const settings = require('./settings'); // 假设这里有一个包含配置的文件
exports.doLogin = async function (req, res) {
if (!req.body.phone || !req.body.email) {
return res.status(400).json({ err: 'Please input your username and password' });
}
const md5 = crypto.createHash('md5');
const password = md5.update(req.body.password).digest('base64');
try {
const user = await User.getByLogin(req.body); // 使用异步函数来处理查询
if (!user) {
return res.status(401).json({ err: 'user does not exist' });
}
if (user.user.userInfo.password !== password) {
return res.status(402).json({ err: 'password does not match' });
}
req.session.cookie.originalMaxAge = settings.maxAge;
req.session.user = user;
return res.status(200).json(user);
} catch (error) {
console.error(error);
return res.status(500).json({ err: 'Internal server error' });
}
};
通过使用异步函数和 try-catch
结构,可以更好地处理可能发生的异常,并确保每个响应都带有明确的状态码。同时,也可以检查 User.getByLogin
方法的实现,确保其效率。