JavaScript 中怎么在 async 函数中处理分布式锁或并发互斥控制

夜雪小哥_3246

夜雪小哥_3246

2026-09-07

123人浏览

原创

分布式锁必须依赖redis等外部系统实现跨进程互斥,async函数本身不提供该能力;核心是用set key value nx px命令原子加锁,配合lua脚本比对value后安全删锁,并需规避单点故障、超时设置不当及非幂等操作等风险。

javascript 中怎么在 async 函数中处理分布式锁或并发互斥控制

在 JavaScript 中,async 函数本身不提供分布式锁能力,因为 JS 运行时(如 Node.js 单进程、浏览器)天然是单线程事件循环模型,无法直接跨进程、跨服务、跨机器协调资源。所谓“分布式锁”,必须依赖外部系统(如 Redis、ZooKeeper、etcd 等)来实现多实例间的互斥。下面分场景说明怎么做、关键点在哪、常见坑怎么避。

为什么不能只靠 await + Promise 实现分布式锁

很多人误以为用 let locked = false 配合 async/await 就能锁住——这是本地内存锁,仅对当前进程有效。一旦服务部署多个副本(如 PM2 集群、K8s 多 Pod),每个实例都有独立内存,彼此完全不知情,锁就失效了。

真正需要的是:一个所有服务实例都信任的第三方存储,能原子性地判断“锁是否存在”并“尝试获取锁”(即 SETNX 或类似语义)。

Alibabacloud Sdk Client Initialization For Java
Alibabacloud Sdk Client Initialization For Java

在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。

下载

用 Redis 实现最简可用的分布式锁(推荐 Redlock 变体)

Redis 是最常用选择,核心靠 SET key value EX seconds NX 命令:只有 key 不存在时才设置,且带自动过期,避免死锁。

  • 获取锁:生成唯一 client_id(如 UUID),执行 SET lock:order:123 "abc-def-456" EX 10 NX;返回 OK 表示抢锁成功
  • 释放锁:不能直接 DEL(怕删了别人持有的锁),要用 Lua 脚本比对 value 再删:
    if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end
  • 续期(可选):若任务可能超时,需另起定时器用 PEXPIRE 延长 TTL(注意仅限自己加的锁)

Node.js 中封装成易用的 async 工具函数

借助 ioredis 或 redis 客户端,封装成 Promise 化接口:

async function withDistributedLock(lockKey, ttlSeconds = 10, fn) {
  const clientId = crypto.randomUUID();
  const client = redisClient; // 复用已连接的 Redis 实例

  // 尝试加锁
  const res = await client.set(lockKey, clientId, 'EX', ttlSeconds, 'NX');
  if (!res) throw new Error('Lock not acquired');

  try {
    return await fn();
  } finally {
    // 安全释放锁(Lua 脚本)
    await client.eval(
      'if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end',
      1,
      lockKey,
      clientId
    );
  }
}

// 使用示例
app.post('/api/order/:id/pay', async (req, res) => {
  const { id } = req.params;
  try {
    const result = await withDistributedLock(
      `lock:pay:${id}`,
      30,
      () => processPayment(id)
    );
    res.json({ success: true, data: result });
  } catch (err) {
    res.status(423).json({ error: 'Resource busy' });
  }
});

生产环境必须注意的细节

简单 SETNX 不足以应对网络分区、主从切换等真实故障:

  • 不要依赖单 Redis 实例:主节点宕机未同步到从节点,新主可能允许重复加锁。建议用 Redis Cluster 或 Redlock(向 ≥N/2+1 个独立 Redis 实例请求锁)
  • 锁的 TTL 必须远大于业务平均耗时,但也不能太长(影响故障恢复速度),建议设为「预期最大执行时间 × 2」
  • 业务代码必须幂等:即使锁失效导致并发进入,也要保证多次执行结果一致(比如支付接口查订单状态再操作,而非无条件扣款)
  • 避免在锁内做不确定耗时操作:如调用第三方 HTTP 接口、大量计算——应尽量缩短临界区,或拆分为“锁内校验 + 异步执行”

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

4166

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1049

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4064

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

840

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1716

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3045

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

3953

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

2520

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

828

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 11.9万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.5万人学习