mongoDB 和 nodeJs 无法正确处理并发请求
Posted
技术标签:
【中文标题】mongoDB 和 nodeJs 无法正确处理并发请求【英文标题】:Concurrent request can not handle properly with mongoDB and nodeJs 【发布时间】:2018-11-25 03:17:51 【问题描述】:我正在开发基于 socket.io room 的项目。我将 socket 与 nodejs 一起使用并在 mongoDB 中管理房间数据。 这是我的代码,只有两个玩家可以加入一个房间,然后我将 IsGameOn 标志设置为 false 为 true。 当我一一向服务器发送请求时,此代码工作正常。 当一次有多个请求时会出现问题。问题是超过 2 个玩家加入房间(房间玩家的数据存储在一个玩家数组中)。
我还上传了数据库的图像。因此,您可以看到数据库中实际发生的情况。
const joinRoom = async (sData, callback) =>
if(sData.iPlayerId && sData.eRoomCount)
try
let body = _.pick(sData, ['eRoomCount', 'iPlayerId']);
console.log(body);
await roomsModel.aggregate([
$match:
eRoomCount: body.eRoomCount,
IsGameOn: $eq: false
,
$unwind: "$aPlayers" ,
$group:
_id: "$_id",
eRoomCount: $first: "$eRoomCount" ,
aPlayers: $push: "$aPlayers" ,
size: $sum: 1
,
$match:
size: '$lt': body.eRoomCount
,
$sort: size: -1
]).exec((error, data) =>
if (data.length < 1)
let params =
eRoomCount: body.eRoomCount,
aPlayers: [
iPlayerId: body.iPlayerId
]
let newRoom = new roomsModel(params);
console.log(JSON.stringify(newRoom));
newRoom.save().then((room) =>
console.log("succ", room);
callback(null,room);
).catch((e) =>
callback(e,null);
);
else
roomsModel.findOne( _id: data[0]._id , (error, room) =>
if (error)
callback(error,null);
if (!room)
console.log("No room found");
callback("No room found",null);
room.aPlayers.push( iPlayerId: body.iPlayerId );
if (room.aPlayers.length === room.eRoomCount)
room.IsGameOn = true;
room.save().then((room) =>
callback(null,room);
).catch((e) =>
callback(e,null);
);
)
);
catch (e)
console.log(`Error :: $e`);
let err = `Error :: $e`;
callback(e,null);
当请求一个接一个时会发生这种情况。
当一次有多个请求时会发生这种情况。
【问题讨论】:
如何访问 MongoDB?你在用猫鼬吗? 是的,我正在使用猫鼬。 旁注,像这样混合promise、callbacks和await/async,势必会导致后期维护混乱。 【参考方案1】:正确的方法是使用猫鼬的findOneAndUpdate
而不是findOne
。操作findOneAndUpdate
是原子的。如果你做正确的查询,你可以让你的代码线程安全。
// This makes sure, that only rooms with one or no player gets selected.
query =
// Like before
_id: data[0]._id,
// This is a bit inelegant and can be improved (but would work fast)
$or:
aPlayers: $size: 0 ,
aPlayers: $size: 1
// $addToSet only adds a value to a set if it not present.
// This prevents a user playing vs. him/herself
update = $addToSet: aPlayers: iPlayerId: body.iPlayerId
// This returns the updated document, not the old one
options = new: true
// Execute the query
// You can pass in a callback function
db.rooms.findOneAndUpdate(query, update, options, callback)
【讨论】:
【参考方案2】:对于这个问题,您可以使用信号量模块,在您的情况下,节点服务器在单线程环境中访问并发请求,但操作系统执行多任务工作。信号量模块将帮助您摆脱这个地狱。
npm i semaphore (安装模块) const sem = require('信号量')(1); (此处要求并指定并发请求限制 其1) sem.take(函数()); (将您的函数包装在其中)sem.leave(); (处理完成后调用它,然后下一个请求 将访问该函数)
const joinRoom = (sData, callback) =>
sem.take(function ()
if (sData.iPlayerId && sData.eRoomCount)
try
let body = _.pick(sData, ['eRoomCount', 'iPlayerId']);
roomsModel.aggregate([
$match:
eRoomCount: body.eRoomCount,
IsGameOn: $eq: false
,
$unwind: "$aPlayers" ,
$group:
_id: "$_id",
eRoomCount: $first: "$eRoomCount" ,
aPlayers: $push: "$aPlayers" ,
size: $sum: 1
,
$match:
size: '$lt': body.eRoomCount
,
$sort: size: -1
]).exec((error, data) =>
if (data.length < 1)
let params =
eRoomCount: body.eRoomCount,
aPlayers: [
iPlayerId: body.iPlayerId
]
let newRoom = new roomsModel(params);
console.log(JSON.stringify(newRoom));
newRoom.save().then((room) =>
console.log("succ", room);
sem.leave();
callback(null, room);
).catch((e) =>
sem.leave();
callback(e, null);
);
else
roomsModel.findOne( _id: data[0]._id , (error, room) =>
if (error)
sem.leave();
callback(error, null);
if (!room)
console.log("No room found");
sem.leave();
callback("No room found", null);
room.aPlayers.push( iPlayerId: body.iPlayerId );
if (room.aPlayers.length === room.eRoomCount)
room.IsGameOn = true;
room.save().then((room) =>
sem.leave();
callback(null, room);
).catch((e) =>
sem.leave();
callback(e, null);
);
);
);
catch (e)
console.log(`Error :: $e`);
let err = `Error :: $e`;
callback(e, null);
);
【讨论】:
这增加了很多样板代码,另一个依赖并降低了应用程序的性能。最好在数据库层面保证数据库的一致性,这可以通过正确的MongoDB函数实现。 但有时我们必须对选定的数据执行多项任务,此时它可能很有用。 可能是这样。关于问题,情况并非如此。在其他未命名的假设情况下,如果这种解决方案是最好的方法,仍然值得怀疑。以上是关于mongoDB 和 nodeJs 无法正确处理并发请求的主要内容,如果未能解决你的问题,请参考以下文章
使用mongodb在nodejs中使用锁定/事务读取和插入文档
使用mongodb在nodejs中使用锁定/事务读取和插入文档