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

如何在Node.js中正确处理并发问题?附多端点共享逻辑示例

如何在Node.js中正确处理并发问题(结合你的Express多端点场景)

嘿,我来帮你梳理下这个问题——你的代码里多个Express端点重复了几乎一样的逻辑,而且还要处理并发安全,咱们先从代码复用入手,再搞定并发问题,这样代码既好维护又安全。

第一步:提取通用逻辑,干掉重复代码

你现在的每个端点都在重复调用asyncFunction和后续的任务逻辑,这完全可以封装成一个独立函数或者Express中间件,以后改逻辑只需要改一处就行:

const express = require('express');
const router = express.Router();

// 把重复的逻辑封装成通用函数
async function handleCommonTask(something, req, res) {
  try {
    // 先把callback风格的asyncFunction转成Promise(用await更顺手)
    const userId = await new Promise((resolve, reject) => {
      asyncFunction(something, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      });
    });

    // 这里写你的通用任务逻辑
    console.log(`正在处理用户ID: ${userId}`);
    // ... 比如数据库操作、业务计算之类的
    res.status(200).json({ success: true, userId });
  } catch (err) {
    // 统一处理错误,不用每个端点都写一遍
    console.error('任务处理出错:', err);
    res.status(500).json({ success: false, error: err.message });
  }
}

// 现在每个端点只需要调用这个通用函数就行,清爽多了
router.post('/test1', (req, res) => {
  handleCommonTask(something1, req, res);
});

router.post('/test2', (req, res) => {
  handleCommonTask(something2, req, res);
});

router.post('/test3', (req, res) => {
  handleCommonTask(something3, req, res);
});

第二步:针对性处理并发安全问题

Node.js是单线程事件循环,但异步操作(比如数据库请求、IO)是并发执行的,所以如果你的通用任务涉及共享资源操作(比如改全局变量、更新同一条数据库记录、读写同一个文件),就会出现竞争条件,得针对性解决:

场景1:操作共享内存(比如全局变量)

如果多个请求同时修改同一个全局变量(比如计数):

let globalCounter = 0;
// 通用任务里的逻辑
globalCounter += 1;

这种情况并发下计数肯定不准,你可以用互斥锁保证同一时间只有一个请求执行这段代码。先安装async-mutex库:

npm install async-mutex

然后修改通用函数:

const { Mutex } = require('async-mutex');
// 创建一个锁实例,专门保护这个计数器
const counterMutex = new Mutex();

async function handleCommonTask(something, req, res) {
  try {
    const userId = await new Promise((resolve, reject) => {
      asyncFunction(something, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      });
    });

    // 加锁,确保这段代码同一时间只有一个请求在执行
    await counterMutex.runExclusive(async () => {
      globalCounter += 1;
      console.log(`当前计数: ${globalCounter}, 处理用户: ${userId}`);
    });

    res.status(200).json({ success: true, userId, counter: globalCounter });
  } catch (err) {
    console.error('任务处理失败:', err);
    res.status(500).json({ success: false, error: err.message });
  }
}

场景2:操作数据库(更新同一条记录)

如果你的通用任务是更新用户余额、库存这类数据,直接并发更新会导致数据不一致,这时候用数据库事务或者乐观锁就很靠谱:

比如用MySQL事务的例子:

async function handleCommonTask(something, req, res) {
  try {
    const userId = await new Promise((resolve, reject) => {
      asyncFunction(something, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      });
    });

    // 开启事务,确保一系列操作要么全成功,要么全回滚
    await db.beginTransaction();
    // 先查询当前值
    const [user] = await db.query('SELECT balance FROM users WHERE id = ?', [userId]);
    // 更新余额(比如扣除10元)
    await db.query('UPDATE users SET balance = ? WHERE id = ?', [user.balance - 10, userId]);
    // 提交事务
    await db.commit();

    res.status(200).json({ success: true, userId, newBalance: user.balance - 10 });
  } catch (err) {
    // 出错就回滚,避免脏数据
    await db.rollback();
    console.error('任务处理失败:', err);
    res.status(500).json({ success: false, error: err.message });
  }
}

或者用乐观锁(通过版本号控制,避免并发覆盖):

UPDATE users SET balance = balance - 10, version = version + 1 WHERE id = ? AND version = ?

这样只有当版本号和查询时一致才会更新,完美避免并发覆盖问题。

场景3:需要同时执行多个异步任务

如果你的通用任务里要同时调用多个API、查询多个数据库表,用Promise.all来并发执行,效率会高很多,同时要注意错误处理:

async function handleCommonTask(something, req, res) {
  try {
    const userId = await new Promise((resolve, reject) => {
      asyncFunction(something, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      });
    });

    // 并发执行多个异步任务,比逐个等待快很多
    const [userInfo, orderList] = await Promise.all([
      db.query('SELECT * FROM users WHERE id = ?', [userId]),
      db.query('SELECT * FROM orders WHERE user_id = ?', [userId])
    ]);

    res.status(200).json({
      success: true,
      userId,
      userInfo: userInfo[0],
      orders: orderList
    });
  } catch (err) {
    console.error('任务处理失败:', err);
    res.status(500).json({ success: false, error: err.message });
  }
}

要是想让某个任务失败不影响其他任务,就用Promise.allSettled,它会返回所有任务的结果(成功或失败),你可以自己逐个处理。

最后总结下

  1. 先把重复逻辑抽出来,代码维护起来轻松太多;
  2. 根据不同并发场景选对应方案:
    • 共享内存:用互斥锁;
    • 数据库操作:事务或乐观锁;
    • 多异步任务:Promise.all/Promise.allSettled;
  3. 错误处理一定要做好,别让一个请求的问题连累其他请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:23:36