今天跟我一位朋友讨论一个防止表单二次提交的问题,他是考虑用 redis,我的解决方案是在本地使用锁来保证请求在处理完成之前同一用户的其他请求会被忽略,代码如下:
private static final Set<String> LOCK_SET = new CopyOnWriteArraySet<>();
public String execute(){
...
synchronized (this) {
if (LOCK_SET.contains(uuid)) {
return "failed";
}
LOCK_SET.add(uuid);
}
...
}
然后就跟他杠上了,他的看法是当其他用户的请求来时会被锁住等待,用 redis 更快。
而我的看法是这个问题用 redis 有点太大材小用了,我认为在这个情况下等待锁的时间是远远小于连接 redis 的开销的。
有大神能帮忙解答一下吗,感激不尽。
1
IGJacklove 2020-08-27 13:30:45 +08:00
感觉 redis 不算大材吧...
|
2
Joezeo OP @IGJacklove 主要我是感觉这个没有必要用 redis..
|
3
kop1989 2020-08-27 13:39:29 +08:00
我没太读懂。
redis 不是一个缓存技术么,只是作用于存储。 对于接口黑名单而言(二次提交也是黑名单,只不过是持续时间很短),不管用什么技术存储黑名单,锁是一定要存在的吧,所以不管用不用 redis,读取黑名单这步骤依然是要队列执行的。 |
4
kop1989 2020-08-27 13:41:19 +08:00
所以你们的争论其实就是 new 对象和用 redis 来存储黑名单的区别?
|
5
Immortal 2020-08-27 13:47:50 +08:00
早起还没有前后端分离的时候 后端会在 form 里渲染一个 key 存在这个 key 并且和服务端一致才让提交
现在前后端分离了 前端可以同样按这个思路 客户端自己维护这个 key 这类解决方案 google 下很多的 用 redis 啥的有点夸张了 |
8
kop1989 2020-08-27 14:02:39 +08:00
@Joezeo #6 如果我理解的没错的话,那优缺点就很明显了。
直接 new 一个对象集合来存储是不可靠的,有可能被 web 容器的垃圾清理释放掉。 redis 相对安全,但 redis 的效率比直接 add 进集合再 remove 肯定是要慢很多。 但还是要强调下,这个话题和锁不锁无关,这种场景肯定是要有锁的,只不过 redis 是单线程,你不需要去操作锁而已。所以你同事说的啥“当其他用户的请求来时会被锁住等待”这个对 redis 也一样适用。 |
9
IGJacklove 2020-08-27 14:03:36 +08:00 via Android
@Joezeo 我感觉你锁接口比用 Redis 重的多,你的问题是单用户的重复提交,你的解决方案是锁全部人的提交。感觉你的问题不是更大吗?假如有一百个人的 200 个请求过来,你要做的就是拦住一百个人的后一百次重复提交,你的解决方案是锁了所有人的提交,这不是明显有问题吗?要是一百个人的一百次请求你根本不需要锁啊
|
10
hahasong 2020-08-27 14:11:08 +08:00
CSRF token 不香吗,提交一次就失效,不刷新页面再提交肯定失败
|
11
013231 2020-08-27 14:26:31 +08:00
如果服務進程不止一個,你的本地鎖要如何解決兩次請求落在兩個不同進程的問題?
|
12
672795574 2020-08-27 14:29:16 +08:00
11 楼说的才是要解决的问题,你本地锁得保证这个用户的请求落到同一台机器上
|
13
wysnylc 2020-08-27 15:33:24 +08:00
sync 在分布式下无用
|
14
ZSeptember 2020-08-27 15:50:18 +08:00
首先看是不是单服务器部署,再看用户量级以及请求量。
单服务器部署,用户量级少,请求量少,可以放内存。 |
15
opengps 2020-08-27 15:52:54 +08:00
推荐保留 redis,因为他可以集群内共享,扩展集群时候不用你额外改动代码
|
16
byzf 2020-08-27 18:17:50 +08:00
目测你这个 add 再 remove 一下, LOCK_SET 大了受不了.
redis 就是用来做这个的, 不算大材小用. |
17
Jooooooooo 2020-08-27 19:19:29 +08:00
这
分布式的锁和内存里单机的锁没有可比性 |