V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Joezeo
V2EX  ›  问与答

求大神解答一下一个小白程序员的疑惑

  •  
  •   Joezeo · 2020-08-27 13:10:48 +08:00 · 1590 次点击
    这是一个创建于 1550 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天跟我一位朋友讨论一个防止表单二次提交的问题,他是考虑用 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 的开销的。
    有大神能帮忙解答一下吗,感激不尽。

    17 条回复    2020-08-27 19:19:29 +08:00
    IGJacklove
        1
    IGJacklove  
       2020-08-27 13:30:45 +08:00
    感觉 redis 不算大材吧...
    Joezeo
        2
    Joezeo  
    OP
       2020-08-27 13:35:36 +08:00
    @IGJacklove 主要我是感觉这个没有必要用 redis..
    kop1989
        3
    kop1989  
       2020-08-27 13:39:29 +08:00
    我没太读懂。
    redis 不是一个缓存技术么,只是作用于存储。

    对于接口黑名单而言(二次提交也是黑名单,只不过是持续时间很短),不管用什么技术存储黑名单,锁是一定要存在的吧,所以不管用不用 redis,读取黑名单这步骤依然是要队列执行的。
    kop1989
        4
    kop1989  
       2020-08-27 13:41:19 +08:00
    所以你们的争论其实就是 new 对象和用 redis 来存储黑名单的区别?
    Immortal
        5
    Immortal  
       2020-08-27 13:47:50 +08:00
    早起还没有前后端分离的时候 后端会在 form 里渲染一个 key 存在这个 key 并且和服务端一致才让提交
    现在前后端分离了 前端可以同样按这个思路 客户端自己维护这个 key
    这类解决方案 google 下很多的 用 redis 啥的有点夸张了
    Joezeo
        6
    Joezeo  
    OP
       2020-08-27 13:56:34 +08:00
    @kop1989 emmm 差不多就是这个意思,我们争论的点就是在这个情况下有没有必要使用 redis 来进行存储
    Joezeo
        7
    Joezeo  
    OP
       2020-08-27 13:59:15 +08:00
    @Immortal 好的,感谢(我也是觉得这个用 redis 有点夸张了)
    kop1989
        8
    kop1989  
       2020-08-27 14:02:39 +08:00
    @Joezeo #6 如果我理解的没错的话,那优缺点就很明显了。
    直接 new 一个对象集合来存储是不可靠的,有可能被 web 容器的垃圾清理释放掉。
    redis 相对安全,但 redis 的效率比直接 add 进集合再 remove 肯定是要慢很多。

    但还是要强调下,这个话题和锁不锁无关,这种场景肯定是要有锁的,只不过 redis 是单线程,你不需要去操作锁而已。所以你同事说的啥“当其他用户的请求来时会被锁住等待”这个对 redis 也一样适用。
    IGJacklove
        9
    IGJacklove  
       2020-08-27 14:03:36 +08:00 via Android
    @Joezeo 我感觉你锁接口比用 Redis 重的多,你的问题是单用户的重复提交,你的解决方案是锁全部人的提交。感觉你的问题不是更大吗?假如有一百个人的 200 个请求过来,你要做的就是拦住一百个人的后一百次重复提交,你的解决方案是锁了所有人的提交,这不是明显有问题吗?要是一百个人的一百次请求你根本不需要锁啊
    hahasong
        10
    hahasong  
       2020-08-27 14:11:08 +08:00
    CSRF token 不香吗,提交一次就失效,不刷新页面再提交肯定失败
    013231
        11
    013231  
       2020-08-27 14:26:31 +08:00
    如果服務進程不止一個,你的本地鎖要如何解決兩次請求落在兩個不同進程的問題?
    672795574
        12
    672795574  
       2020-08-27 14:29:16 +08:00
    11 楼说的才是要解决的问题,你本地锁得保证这个用户的请求落到同一台机器上
    wysnylc
        13
    wysnylc  
       2020-08-27 15:33:24 +08:00
    sync 在分布式下无用
    ZSeptember
        14
    ZSeptember  
       2020-08-27 15:50:18 +08:00
    首先看是不是单服务器部署,再看用户量级以及请求量。
    单服务器部署,用户量级少,请求量少,可以放内存。
    opengps
        15
    opengps  
       2020-08-27 15:52:54 +08:00
    推荐保留 redis,因为他可以集群内共享,扩展集群时候不用你额外改动代码
    byzf
        16
    byzf  
       2020-08-27 18:17:50 +08:00
    目测你这个 add 再 remove 一下, LOCK_SET 大了受不了.

    redis 就是用来做这个的, 不算大材小用.
    Jooooooooo
        17
    Jooooooooo  
       2020-08-27 19:19:29 +08:00


    分布式的锁和内存里单机的锁没有可比性
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2668 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 05:31 · PVG 13:31 · LAX 21:31 · JFK 00:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.