lmk123 / blog

个人技术博客,博文写在 Issues 里。
https://github.com/lmk123/blog/issues
623 stars 35 forks source link

被 Node.js 的异步上了一课 #98

Open lmk123 opened 2 years ago

lmk123 commented 2 years ago

事情是这样的。用户在划词翻译里可以选择付费使用翻译服务,后台里扣费的代码如下:

// 执行翻译功能的路由
router.get('/translate', async (ctx) => {
  const { apiName, userId } = ctx
  console.log('开始调用' + apiName)
  const user = await UserModel.findOne(userId)
  console.log('用户的余额为:', user.balance)
  // 从翻译服务那里获取翻译结果,并返回需要扣除的费用
  const { result, price } = await doTranslate(apiName)
  console.log(`${apiName}扣除的费用为:`, price)
  // 从用户的余额里扣除费用
  user. balance -= price
  console.log('用户的剩余余额为:', user.balance)
  // 保存进数据库
  await user.save()
  ctx.body = result
})

这段代码在生产环境运行到现在,直到我今天无意中发现一个问题:我同时开启了多个翻译服务,但只扣了一次费用。

细心的同学可能已经直到问题出在哪里了……

当用户同时启用了多个翻译服务时,客户端实际上同时发起了多个翻译请求,也就是说,控制台的日志是这样的:

开始调用有道翻译
开始调用百度翻译
用户的余额为: 100
用户的余额为: 100
百度翻译扣除的费用为:1
有道翻译扣除的费用为:1
用户的剩余余额为:99
用户的剩余余额为:99

这也就导致了为什么按照预期应该扣除 2 的场景下,最终结果却只扣除了 1……

本来以为作为一个前端来说,对于异步已经得心应手,但现在已经完全不敢有这个自信了 :joy:

zbysir commented 2 years ago

哈哈,不过这也不是异步的问题,是并发的问题(“并发扣费”),任何语言都会遇到这个问题。