后端如何处理接口幂等性?

2018-10-25 12:41:15 +08:00
 HarryQu

最近在搞 Spring Boot ,遇到了这样的一个问题 : 菜鸡如我,想问问大家是如何处理的。

场景 :

有个移动端的用户,手特别快, 注册的时候瞬间连点 100 下 ,发出了 100 个请求 :


问题 :

@Service
@Transactional
public class UserService {

    @Autowired
    UserDao userDao;
    
    public boolean register(User user) {
        Optional<User> user = userDao.findByTelephone(user.telephone);
        if (user.isPresent()) {
            throw new IllegalStateException("账号已存在");
        }
        userDao.save(user);
        return true;
    }
}

register 是非线程安全的 ,这样就导致该用户重复注册,数据库中有多条数据。


我的解决方案 : 加锁... 但是我觉得效率上个问题

大家是如何处理的呢 ?

8503 次点击
所在节点    程序员
38 条回复
realityone
2018-10-25 12:44:26 +08:00
手机号加个唯一索引
helloworld12
2018-10-25 12:45:45 +08:00
前端设置为短时间内只发送一个请求,
服务端根据填写的信息 hash 计算出唯一 ID
lastpass
2018-10-25 12:49:14 +08:00
首先在前端,点击一下之后就 disable,在后台数据没有返回之前不恢复。
后端,加消息队列。
用锁的话,加锁解锁的开销有点大。
p2pCoder
2018-10-25 12:51:59 +08:00
单机的话
```
synchronized (user.telephone.intern())
```
分布式的话,直接给
给 telephone 用 redis 加锁就行了

性能影响不大吧
HarryQu
2018-10-25 12:59:35 +08:00
@realityone 我觉得这应该和索引么关系吧.
awanabe
2018-10-25 13:01:23 +08:00
@HarryQu unique 索引可以直接在 db 层面上报错了...除了 insert 成功的那条之外..别的都返回 db 错误了...
xuanbg
2018-10-25 13:02:25 +08:00
这种非正常的请求,通过限流来防重发就可以了。然后,注册方法中可以增加对手机号是否已存在的判断,如已存在,就返回一个用户已存在的错误信息。
HarryQu
2018-10-25 13:03:06 +08:00
@lastpass
因为我之前也是做前端的,这种问题,还得后端处理, disable 后, 用户立即退出又重新进入界面,再次提交数据。当然前端可以再次判断,只不过类似场景太多,写起来前端比较麻烦。
消息队列的话,我还真是没用过,我研究下,谢谢。
HarryQu
2018-10-25 13:07:59 +08:00
@awanabe @realityone 好的 , 我试试。
jjwjiang
2018-10-25 13:08:49 +08:00
既然 telephone 不能重复,那 DB 里就要 unique 呀,这个 error 完全可以通过 db 抛出来
IssacTomatoTan
2018-10-25 13:09:28 +08:00
后端做 体验 安全一次搞定
lastpass
2018-10-25 13:38:15 +08:00
回复 @HarryQu 前端防君子不防小人,但能够过滤掉大部分问题。最终还是要后端来处理数据。前后端都要做。
HarryQu
2018-10-25 13:42:04 +08:00
@lastpass 嗯 有道理
linbiaye
2018-10-25 13:58:00 +08:00
1. 手机号加 unique, save 以后再 select 看看。
linbiaye
2018-10-25 14:02:54 +08:00
@linbiaye
1. 手机号 unique
2. 控制一下事务级别(MySQL READ COMMITTED),save 以后再 findByTelephone 看看是不是有大于 1 条记录。
reus
2018-10-25 14:03:00 +08:00
当然是数据库加唯一索引

如果这是线上服务,那……祝贵司好运,为啥不找合格的后端来做?
98jiang
2018-10-25 14:31:05 +08:00
@reus 请问一下,合格的应该怎么做呢?
HarryQu
2018-10-25 15:23:54 +08:00
@linbiaye 谢谢,又学到了一点事务级别。
p2pCoder
2018-10-25 15:36:27 +08:00
依据我浅薄的经验来看,把这种幂等性教研扔给 db 的唯一索引来做不合适,
即使有唯一索引,也最好在应用程序中加锁来实现
ppyybb
2018-10-25 15:49:35 +08:00
数据库做唯一索引,当然二级 key 有唯一索引在 innodb 里面也会增加一点开销,会为插入的时候在二级索引上增加隐式锁(好像是的),但是开销应该较小。

前端做控制,减少大部分误操作了。

如果还不想加唯一的限制(万一有啥奇葩的需求..),那么我觉得每 30 分钟设置一个 redis 的 bloomfilter 去重吧,这样可以保证如果点过了就一定会被发现,没点过才去 db 检查。这样基本可以保证没有任何问题了。(实际上我觉得数据库做唯一索引基本上不可能出现问题了,而且很简单)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/501000

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX