node 如何和处理同一用户并发的两个一模一样的请求?
最近碰到个问题,就是同一个用户几乎同时的发起两次同样的请求,比如买一样东西有一定几率会出现购买两次的情况,代码逻辑判断没错,问题出在判断时序上面。下面是一段购买商品的逻辑判断:
'buyItem': async (req, res, next) => {
const itemId = req.params.itemId;
const userId = req.headers['current-user-id'];
const orderRecord = await Order.find(itemId, userId)//do mongodb search
if (orderRecord && orderRecord.status === 'paid') {//第二次请求对于这个是否购买过的判断的结果是未购买,因为第一个请求还未创建订单。
return res.status(400).json({
'code': 101,
'message': 'you have already bought it',
'result': {}
})
}
// 第一次请求执行到这里了
const orderNew = await Order.create(itemId, userId)//do mongodb create
await User.deduct(userId, item.price);
}
两次几乎同时的请求,但有一定的先后顺序,第一个请求执行到即将创建订单,但第二个请求因为第一个请求还未创建,导致判断上不符合预期。
我现在的想法是:
方法一:把创建订单放到最前面,因为 mongodb 有读写 latch,所以判断订单是否存在来决定是否进行后续操作,这样虽然能解决比较关键的路由,但并不普适。
方法二:对访问频率进行限制(rate-limit),但因为我使用了 cluster,不好做。
后端采用:node express
不知道各位大神有没有相似情况的解决方案.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
加载页面的时候生成一个token,提交订单的时候一起提交,服务端接受后进行校验!
如果服务端已经处理了该token,那么抛弃当前请求;否则就处理请求
最近做了个限时抢购的功能,也遇到了同样的问题,由于并发导致的资源竞争问题,也就是“狼多肉少”,处理的思路,对肉做限制,还是对狼制定规则,一个一个来。技术上就是 1.对数据资源做限制 2.制定访问的规则,先到先到,依次处理,数据库层面:利用数据库锁的机制,限制其它没它请求 代码层面:处理思路也很多吧,目前就想到了三种方案,第一:前端做限制 第二:利用队列,将并发问题转化为串行执行, 第三:实现锁的机制,将资源和任务加锁,任务结束,解锁,利用redis可以实现一个锁的机制。方案选择要看并发量和具体的业务场景,目前还没具体的实现代码,只简单的描述了下,自己的一点思路和看法
是否可以在前端做判断呢,比如说,一个请求发出时,还未响应,这段时间,按钮无法再次点击.当然,这只适合正常流程,秒杀活动什么的不适合
这个需要去重处理的,防止订单重复不仅需要前端进行控制,后端也要控制(有时候前端会有非法操作),后端处理:当一个订单来的时候,需要把未处理的订单的信息保存起来的中,(这里可以保存在一个全局变量中,推荐保存在数据库中:比如 redis中 ),根据用户的
ID
进行标别,当下一个相同用户在来订单时候,查询是否未处理的订单 和 比较两个订单的时间间隔(这里可以自定义时间间隔进行去重合并)。当处理完订单后,在根据需求处理上一步保存的未处理的订单信息。可以把请求加入队列,保证先后顺序,然后在一个一个对队列的请求做处理,并行变串行。
加锁
并发小的话可以加悲观锁试试
@豆腐居士 的答案只适合对单用户的请求做排重,要想保证多客户端高并发下的资源(商品)状态维护,还需要服务端服务在处理时加锁,如果是分布式服务还需要redis或zookeeper来实现分布式锁。客户端请求的排重和商品防止二次购买是两个方面的问题。
之前遇到过类似的问题,我是通过加锁来处理的