Rails 关于如何避免客户端同一时间提交重复请求的问题

wfwdex · 2017年07月31日 · 最后由 dddd1919 回复于 2017年08月11日 · 6157 次阅读

我遇到的问题如下:

iOS 客户端因为第一次打开应用需要用户允许网络访问权限,导致第一次发送请求(用于分配帐号)总是在同一时间(1 秒钟内)重复发送多次(2-3 次)。

这样就导致服务端会为这个 uuid 的设备创建多个分配帐号,虽然产品逻辑上是允许一个 uuid 通过另一个注册接口去注册多个帐号,但是并不允许在第一次打开 app 的时候分配多个帐号,所以就导致我无法通过为 uuid 创建数据库约束来避免这种情况。

请问如果不从客户端下手,API 后端如何避免让一个 uuid 在 1 秒钟内同时创建多个帐号?


在写完这个帖子后,我想到一个办法,就是为uuidcreated_at创建联合唯一约束,本着深入研究的精神,想请求除了这个还有别的更好办法吗?

为 uuid 和 created_at 创建联合唯一约好像不行吧?created_at 的精度不是秒。 一个做法是生成账号后,缓存里面给这个 uuid 设个值,以一个过期时间。每次建的时候先查缓存,没有再建新的。

增加一列“注册途径”,用“注册途径”和 uuid 作为唯一约束,created_at 从逻辑上来不合适。

你这个业务上本来就没限制这种东西,所以就没办法限制,否则就要改业务逻辑 一个 uuid 只能创建一个账户

nowherekai 回复

created_at 的类型是 datetime,精度应该就是秒吧?

在缓存里设置 uuid 肯定不行,同一秒发生的请求,缓存也会同时两个一起查和写,

sefier 回复

同时发生的两次请求,完全是一样的,并没有注册途径的区分。

wfwdex 回复

IOS 客户端发的请求,增加一个“ios”的标识,作为注册途径,那么做了唯一索引之后,第二个注册请求就会失败了。

"在缓存里设置 uuid 肯定不行,同一秒发生的请求,缓存也会同时两个一起查和写" 加锁就行了

nowherekai 回复

默认不加参数的 datetime 后面并没有小数,mysql 5.7,刚测试给uuidcreated_at加联合唯一索引,用批量插入语句,uuidcreated_at一样的情况下,触发了Duplicate entry '96f2bfe9-3ed2-4721-a7c8-57185e283fc2-2017-06-08 19:39:41' for key 'uuid_created_at'

说明是可用的办法。

为什么 不把 APP 的 bug 修了?

我跟楼主遇到过一样的问题。这个在后端加一个重复请求拒绝的功能就好了,一模一样的参数你 1 秒内已经收到过,你就返回 400。不过前提是你的参数里面,要带上区分用户的东西,比如用户的 token,或者设备的 uuid,或者加上对方网络的 ip 地址。 实现的方法就很多了,rails 的缓存也行,redis-object 也有类似的存储功能。

dddd1919 回复

有时候客户端很多这种重复提交的问题,不只是一个接口调用,也有 web 端的按钮不加点击后禁用这类问题,你没法控制这么多客户端的页面,所以后端还是得有自己的机制。

happyming9527 回复

客户端的锅不能都砸到后端,这种问题应该先从客户端下手解决问题,web 点击不禁用那就改,不是所有地方都要必须要检验,关键流程比如提交控制好就行,自己的产品这些做不好说不过去吧

然后是后端对提交特征值的校验,比如设备 id,时间戳,ip

需要 登录 后方可回复, 如果你还没有账号请 注册新账号