1
sujin190 2020-06-25 16:55:47 +08:00
对用户 ID 进行加锁就是了啊,如果复杂业务的还可以以一系列信息生成 hash id 来加锁,除了多线程常规的单进程锁外,也可以用 redis 、zookeeper 之类的外部服务来加锁也很方便的
https://github.com/snower/slock 用 go 实现过一个能用于此种场景的小服务,能实现锁的语义还挺多的,性能也不错 |
2
reus 2020-06-25 16:59:26 +08:00
有区别吗?
秒杀是锁商品,签到是锁用户,都是一样的逻辑,可以用一样的方法 |
3
abcbuzhiming OP @sujin190 我不太清楚 Go 的做法,但是 java 好像没找到这种方式
|
4
abcbuzhiming OP @reus 有区别的朋友,这个问题我也是思考过后才提的,秒杀场景里,所有用户争抢一把锁,用户和用户之间有数据冲突。签到场景里,你要防止的是用户抢自己的数据,用户和用户之间是不存在数据冲突的。
所以你说签到锁用户,在 Java 里,这到底是怎么个锁法?才能即避免用户自己和自己的数据冲突,但是锁不影响到其它用户 |
5
phx13ye 2020-06-25 18:36:29 +08:00 via Android
你对用户 id 和日期做幂等啊
|
6
EminemW 2020-06-25 19:11:19 +08:00
这跟语言没关系吧。
用 date_userId 作为 key,存到 redis 里当锁,不就能解决了。 |
7
skypyb 2020-06-25 19:18:26 +08:00
用中间件(zk 、redis) 就行了。业务线名字+用户 ID 拼出锁的 key 去获取就 OK
|
8
yukiloh 2020-06-25 19:26:32 +08:00 via Android
原来我遇到的点的快了给 2 次奖励是这么产生的…
我觉得让前段做个判断,点了就 didable 不就好了,锁后台多麻烦 |
9
chenqh 2020-06-25 20:08:06 +08:00
感觉直接 redis 锁就可以了吧,不管是锁所有人的,还是锁某个人的
|
10
abcbuzhiming OP @phx13ye 朋友,幂等指的是结果,而不是实现这个结果的过程,我问的是“要如何实现这个幂等”?
|
11
abcbuzhiming OP |
12
phx13ye 2020-06-25 21:12:46 +08:00 via Android
@abcbuzhiming 你不借助第三方,你的应用是单机跑的吗?
|
13
abcbuzhiming OP @yukiloh 前端加重复提交令牌确实可以把用户重复请求挡在前端(正常操作情况下),但是并不代表说后端就不需要防御了,实际上当后端规模拉大一点,某些内部事件激发的处理代码确实可能因为种种原因,被同时调用了多次,此时幂等是必要的。问题在于如何实现这个幂等,我比较贪心,想在 JVM 层面上解决这个问题,同时隔离影响,不影响到其它用户
|
14
abcbuzhiming OP @phx13ye 做研究嘛,有时候要看看一个语言到底能做到什么程度。否则 mysql 满地走的年代,为啥还有人用 java 写了个个叫 H2 的内存数据库呢?
|
15
phx13ye 2020-06-25 21:25:42 +08:00 via Android
@abcbuzhiming 。。。。看下 Semaphore 和 guava 的 Ratelimit 吧
|
16
siweipancc 2020-06-25 21:28:28 +08:00 via iPhone
:D 不用 redis? 那共享一个 map 实例行不行,setIfAbsent
|
17
cheng6563 2020-06-25 21:31:49 +08:00 via Android
单进程应用可以用个 Map 存 Lock 对象加锁
|
18
Ezez 2020-06-25 23:58:27 +08:00 via iPhone
是不是可以数据库里加个字段表示当天是否签到?
|
19
nuk 2020-06-26 00:32:20 +08:00
这个可以不用锁啊,既然只执行一次,弄个 counter,atomic fetchadd 一下,等于 1 就执行,不等于就退出。。
|
20
gaius 2020-06-26 00:37:44 +08:00 via Android
🐶一个用户一个锁呗
|
21
Licsber 2020-06-26 00:59:33 +08:00
为啥不签到只更新今日签到标记
然后用一个单线程在那定时获取已经签到且未发送奖励的 发送完奖励后在这个线程中更新已经发送标记 |
22
aguesuka 2020-06-26 09:46:18 +08:00
单应用可以用这个
https://stackoverflow.com/questions/5639870/simple-java-name-based-locks All those answers I see are way too complicated. Why not simply use: public void executeInNamedLock(String lockName, Runnable runnable) { synchronized(lockName.intern()) { runnable.run(); } } The key point is the method intern: it ensures that the String returned is a global unique object, and so it can be used as a vm-instance-wide mutex. All interned Strings are held in a global pool, so that's your static cache you were talking about in your original question. Don't worry about memleaks; those strings will be gc'ed if no other thread references it. Note however, that up to and including Java6 this pool is kept in PermGen space instead of the heap, so you might have to increase it. There's a problem though if some other code in your vm locks on the same string for completely different reasons, but a) this is very unlikely, and b) you can get around it by introducing namespaces, e.g. executeInNamedLock(this.getClass().getName() + "_" + myLockName); |