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);
    });
};

4 回复

Node.js Express 使用 res.json() 客户端出现 Request Timeout 的问题

问题描述

在使用 Express 框架处理用户登录请求时,客户端(iOS 端)在第一次登录时频繁出现 Request Timeout 错误,而在第二次登录时会立即成功。然而,从 Node.js 端的日志来看,响应已经成功发送。

分析

通过 Charles 抓包工具观察到,客户端发送 /login 请求后,服务器返回了响应头但没有返回响应体。这表明请求可能在某些情况下没有完全处理完毕或响应被阻塞了。

可能的原因

  1. 异步操作未完成:在处理用户登录逻辑时,可能存在某些异步操作(如数据库查询)尚未完成,导致响应未能及时返回。
  2. 资源竞争或阻塞:在高并发场景下,可能因为资源竞争或阻塞导致响应延迟。
  3. 网络延迟或客户端超时设置:客户端的超时时间设置过短,导致在正常响应之前超时。

示例代码与改进措施

以下是改进后的示例代码,确保所有异步操作都已完成后再发送响应。

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}`);
});

解释

  1. 异步处理:使用 async/await 语法确保所有异步操作(如数据库查询)都已完成后再发送响应。
  2. 错误处理:添加 try/catch 块来捕获并处理可能出现的异常,避免服务器崩溃。
  3. 状态码:使用 res.status(code).json(data) 方法来明确返回状态码和数据。

通过这些改进,可以有效减少因异步操作未完成或资源竞争导致的响应延迟问题,从而解决客户端的 Request Timeout 问题。


密码没加盐啊貌似。

试着不用 ios 客户端,换个方式测测看 node server 导致有没有问题嘛。既然 node 的 log 里显示成功的话,我觉得应该去调下客户端。

恩,没加盐。密码传输也为明文,通信协议也没有。。。 关键是 waiting for response body 这个连接状态很诡异啊。

根据你的描述,问题可能出现在以下几个方面:

  1. 数据库查询慢User.getByLogin 这个方法可能因为数据库查询效率低导致响应时间过长。可以通过优化查询语句或者添加索引来提高查询效率。

  2. 服务器配置:确保服务器能够处理高并发请求,例如增加 worker 线程数量或者优化 Node.js 的 event loop 配置。

  3. iOS 客户端超时设置:检查 iOS 客户端的网络请求超时设置,确保它足够长以覆盖正常的响应时间。

  4. 日志分析:查看 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 方法的实现,确保其效率。

回到顶部