创buildlocking,以便两个用户不使用相同的用户名创build帐户

我正在使用带有Node.js的Express服务器,并关心并发性,并防止用户同时创build具有相同用户名的帐户。 从我的underatanding,尽pipe主Node.js运行时是单线程的事实,它仍然是可能的一个function开始,而另一个尚未完成。 一种select是在Redis等外部源上创build一个锁,并使用它来控制并发 – 但是我的问题是 – 是否有一种可靠的方法来在本地JavaScript中控制这种情况? 例如,嵌套函数的某个级别基本上可以保证新请求在另一个完成之前不会启动某个调用(我怀疑它)。

更具体的说 – 我正在使用MongoDB的MongoDB。 使用Mongoose模式的内存中应该有唯一的用户列表。 那么会发生什么:

(1)新用户使用用户名A进行注册

(2)node.js将A与索引的用户列表进行比较,发现A确实是一个新的用户名

(3)node.js进行数据库调用,将A添加到列表中

(4)之前(3)完成一个新的用户注册用户名A … node.js比较A与用户名索引,它看起来是唯一的,所以它发送一个插入数据库

这个问题似乎是真的。 我的猜测是解决这个问题的最好办法是外部locking。

解决这个问题最直接的方法是在相应的mongo集合上有一个唯一的索引 。 这将防止任何插入或更新,将导致重复值(请注意,这是由mongodb服务器,而不是mongoose强制执行)。

我不熟悉mongoose,但是看起来,当你告诉它一个属性是唯一的,它会创build相应的唯一索引。

正如你所怀疑的那样,尽pipenode.js是单线程的,只要你的代码参与任何asynchronous操作(如任何数据库操作),那么在另一个请求开始处理的asynchronous操作期间将有机会。 所以,你确实可以同时在飞行中得到两个(或更多)冲突的操作。

这里有几个主要的方法。

  1. 数据库。 让正在创build和存储用户标识的数据库强制执行唯一性。
  2. 连载。 序列化所有可能相冲突的操作。
  3. 机上控制。 保留您自己的“机内”用户名的内存列表。

我会更详细地描述每一个。

数据库。 由于数据库本身是用户名的最终logging,所以有时候最简单的办法就是告诉第二个请求试图创build一个用户名,因为它已经存在了,所以不能创build用户名。 当你这样做的时候,你只是让第一个请求在win和任何后续的只是得到一个错误,因为用户名已经存在。 虽然您可能不会将此作为唯一的保护措施,因为在提交新的用户名之前,先让用户知道它已经在使用中,然后再通知用户,但在并发情况下,它可能是您的后盾。

您在步骤1),2)和3)中描述的方法由于您怀疑的并发问题而不是执行数据库操作的好方法。 如果数据库可以在你的步骤之间改变,并且会使你的逻辑无效或者导致你做错误的数据库操作,那么逻辑必须改变。

相反,你想创build一个成功或失败的primefaces操作。 所以,你想在数据库中创build新build议的用户ID。 如果它成功地创build了一个新的用户ID,那么你很好,它一定是唯一的,现在已经代表这个请求被创build了。 那里没有并发问题。 如果用户标识已经存在,那么创build操作就会失败。 您不想testing用户ID,然后决定创build它,因为它创build了并发窗口。 devise你的数据库操作是完全primefaces的。 一个数据库操作成功或失败。 然后你让数据库处理任何瞬间locking,并让它做的辛苦工作。

序列化操作。 您可以实现自己的stream量,一次只允许一个请求到您的数据库创build一个新的用户。 这将使任何其他请求等待处理中的请求完成。 从可扩展性和吞吐量的angular度来看,这并不理想,但概念上很简单。

手动机上控制。 假设您没有运行集群服务器,所以只有一个Web服务器在您的数据库中创build帐户,您可以为任何正在创build的用户名保留一个内存数据结构。 当有任何数据库请求来创build一个用户名,你可以首先检查你的内存数据结构,看看是否有一个请求已经在创build这个用户名。 如果是,则拒绝新的请求。


由于用户名的权威来源是数据库,它必须有自己的并发控制,我的首选是让数据库告诉第二个请求创build用户名yyy,用户名已经存在,并让该错误渗透回最终用户。 这种方法没有边缘情况和最佳的性能configuration文件。 您仍然可以实施飞行前检查,以便为用户提供对已经存在的用户名的最佳和最直接的反馈,但在出现并发冲突的情况下,将该数据库用作反向站点。

请注意,如果以这种方式实现它,则必须确保您的error handlingpath在服务器和客户端都是干净的。 服务器将不得不部分创build一个帐户,然后错误并将其保持在一个不良状态,客户端将不得不知道如何正确显示错误情况以及将用户置于何种状态。

您可以创build一个保留或待定名称的列表。

var pendingNames = []; 

当用户A想要使用用户名“蝙蝠侠”进行注册时,您将首先检查名字本身是否被占用。

如果名字没有被使用,你将检查名字是否在待处理的名字列表中。

 var name = "Batman"; if(pendingNames.indexOf(name.toLowerCase()) !== -1) { //abort the registration } // if its not in the pending list, simply add it to it pendingNames.push(name.toLowerCase()); 

然后写入成功或失败后,从待处理列表中删除名称。

还有其他的方式,但我发现这是最快速的快速validation,如果一个名字可以注册或不。