You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Node.js Express的双客户端非轮询请求通信实现问询

这需求太常见了——要做一个长等待的HTTP请求,不用轮询,等数据变更再返回对吧?我给你一套靠谱的实现方案,基于Express和Node.js的事件发射器,完美适配你的场景:

实现方案:基于事件发射器的挂起请求处理

核心思路是把第一个客户端的请求临时挂起,用事件绑定的方式等待数据变更信号,当第二个客户端完成数据库更新后,触发对应事件唤醒挂起的请求,直接返回响应。

1. 基础依赖与初始化

先装好必要的包(这里用MySQL做数据库示例,你可以换成MongoDB、PostgreSQL等任意数据库):

npm install express mysql2

然后初始化Express应用和事件发射器:

const express = require('express');
const mysql = require('mysql2/promise');
const { EventEmitter } = require('events');

const app = express();
app.use(express.json());

// 创建事件发射器实例,用来监听数据变更事件
const dataChangeEmitter = new EventEmitter();
// 解除默认监听器数量限制,避免大量等待请求时的警告
dataChangeEmitter.setMaxListeners(0);

// 数据库连接配置,替换成你的实际信息
const dbConfig = {
  host: 'localhost',
  user: 'your_db_user',
  password: 'your_db_password',
  database: 'your_db_name'
};

2. 第一个客户端的等待请求接口

这个接口会先写入初始数据,然后挂起等待对应uniqueid的数据变更事件:

app.post('/wait-for-update/:uniqueid', async (req, res) => {
  const { uniqueid } = req.params;
  const initialData = req.body;

  try {
    // 1. 连接数据库,写入初始数据(支持重复写入时覆盖)
    const connection = await mysql.createConnection(dbConfig);
    await connection.execute(
      'INSERT INTO your_table (uniqueid, data) VALUES (?, ?) ON DUPLICATE KEY UPDATE data = ?',
      [uniqueid, JSON.stringify(initialData), JSON.stringify(initialData)]
    );
    await connection.end();

    // 2. 创建Promise,等待数据变更事件触发
    const waitForChange = new Promise((resolve, reject) => {
      // 30秒超时保护,避免客户端无限等待
      const timeoutTimer = setTimeout(() => {
        reject(new Error('等待超时,请重试'));
      }, 30000);

      // 绑定一次性事件监听器,触发后自动移除
      dataChangeEmitter.once(`data-updated:${uniqueid}`, (updatedData) => {
        clearTimeout(timeoutTimer);
        resolve(updatedData);
      });
    });

    // 3. 等待事件触发后,返回响应给第一个客户端
    const updatedData = await waitForChange;
    res.json({
      status: 'success',
      message: '数据已更新',
      data: updatedData
    });
  } catch (err) {
    console.error('等待请求出错:', err);
    res.status(500).json({ status: 'error', message: err.message });
  }
});

3. 第二个客户端的更新数据接口

这个接口更新数据库记录后,直接触发对应uniqueid的事件,唤醒挂起的请求:

app.post('/update-data/:uniqueid', async (req, res) => {
  const { uniqueid } = req.params;
  const updatedData = req.body;

  try {
    // 1. 连接数据库,更新对应记录
    const connection = await mysql.createConnection(dbConfig);
    const [result] = await connection.execute(
      'UPDATE your_table SET data = ? WHERE uniqueid = ?',
      [JSON.stringify(updatedData), uniqueid]
    );
    await connection.end();

    if (result.affectedRows === 0) {
      return res.status(404).json({ status: 'error', message: '未找到对应记录' });
    }

    // 2. 触发数据变更事件,把更新后的数据传递给等待的请求
    dataChangeEmitter.emit(`data-updated:${uniqueid}`, updatedData);

    res.json({ status: 'success', message: '数据已更新' });
  } catch (err) {
    console.error('更新请求出错:', err);
    res.status(500).json({ status: 'error', message: err.message });
  }
});

// 启动服务器
app.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
});

4. 额外优化建议

  • 数据库原生变更监听:如果你的数据库支持(比如MySQL的Binlog、MongoDB的Change Streams),可以直接监听数据库的变更事件,不用通过第二个请求触发,可靠性更高。
  • 请求上下文清理:如果请求中途断开(比如客户端关闭页面),可以通过res.on('close')事件清理对应的事件监听器,避免内存泄漏。
  • 集群模式适配:如果用Node.js集群模式,事件发射器无法跨进程通信,这时候可以用Redis的Pub/Sub替代本地事件发射器。

测试步骤

  1. 启动服务器:node server.js
  2. 第一个客户端发送等待请求(用curl或Postman):
    curl -X POST -H "Content-Type: application/json" -d '{"value":"初始值"}' http://localhost:3000/wait-for-update/abc123
    
    此时请求会处于挂起状态。
  3. 第二个客户端发送更新请求:
    curl -X POST -H "Content-Type: application/json" -d '{"value":"更新后的值"}' http://localhost:3000/update-data/abc123
    
    第一个请求会立刻返回更新后的JSON响应。

内容的提问来源于stack exchange,提问作者Heichou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:08:59