请教一个问题,多实例同时删除 key 怎么解决

2019-10-14 10:27:39 +08:00
 UserNameisNull

背景:redis放了微信的access_token,缓存时间是expire_time - 200s,当有新的token生成,旧的就会失效。

服务有多个实例,取到access_token后请求微信接口,微信接口报token失效的错误,就从redis中删除 key 并更新为最新的。

就会遇到 A 实例拿到一个token,B 实例把token更新了,就会导致 A 实例报错。

有大佬可以提供一个思路吗?

3285 次点击
所在节点    程序员
35 条回复
optional
2019-10-14 10:33:01 +08:00
多个实例请求微信接口前先判断下 redis 里的 token ? 这是最简单的吧。
搞个 pub/sub 就有点多余了。
xuanbg
2019-10-14 10:45:29 +08:00
分布式锁,更新的时候加锁就行了
julyclyde
2019-10-14 10:50:24 +08:00
具体到你这个案例,其实解决方法是只更新不删除
zisway
2019-10-14 10:53:07 +08:00
可以提前刷新,不依赖 redis 过期。存储时,保存 key 和 key 的创建时间。判断创建时间,是否进行提前更新。
更新时拿获取到的创建时间去更新。如果时间一致,则去 wx 获取 key 更新,否则说明被别的实例更新过了。
Vegetable
2019-10-14 10:53:46 +08:00
建议开发者使用中控服务器统一获取和刷新 access_token,其他业务逻辑服务器所使用的 access_token 均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致 access_token 覆盖而影响业务;
Vegetable
2019-10-14 10:55:01 +08:00
还有一句

access_token 的有效期通过返回的 expire_in 来传达,目前是 7200 秒之内的值,中控服务器需要根据这个有效时间提前去刷新。在刷新过程中,中控服务器可对外继续输出的老 access_token,此时公众平台后台会保证在 5 分钟内,新老 access_token 都可用,这保证了第三方业务的平滑过渡;

https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
IMCA1024
2019-10-14 11:35:56 +08:00
赞同 5 楼的
UserNameisNull
2019-10-14 12:53:47 +08:00
@Vegetable 中控服务器应该是实例吧,如果多实例,又会出现上面的提到的问题。
单实例要保证高可用,高稳定
UserNameisNull
2019-10-14 12:56:08 +08:00
@optional
肯定判断了的。
比如 redis 中 token-1 正常没到过期时间,
A 实例 get token-1, B 实例 get token-1,
A 发现 token-1 已过期,A 会更新。然后 B 也发现 token-1 过期,B 也更新了,就会导致重复更新。
UserNameisNull
2019-10-14 12:57:56 +08:00
@xuanbg
一样的问题,
比如 redis 中 token-1 正常没到过期时间,
A 实例 get token-1, B 实例 get token-1,
A 发现 token-1 已过期,获取分布式锁,A 会更新。
然后 B 也发现 token-1 过期,等待获取锁,得到锁,B 也更新了,就会导致重复更新。
mango88
2019-10-14 13:00:45 +08:00
用分布式锁呗,三个实例,谁持有锁,谁更新 access_token
mango88
2019-10-14 13:05:29 +08:00
三个实例 ,尝试 set 同一个 key, value 为实例 id ;
set 成功直接跟新 access_token,
set 失败判断 , 判断 id 是否属于自己的实例,属于自己就更新 token,并刷新 expire time
UserNameisNull
2019-10-14 13:14:57 +08:00
@mango88 感谢提供思路
hdbzsgm
2019-10-14 13:18:30 +08:00
@UserNameisNull #10 仔细走一下分布式锁的逻辑 比如 B 得到锁之后 要不要先查询当前 token 是否过期的 再去执行更新逻辑 当然靠时间是不靠谱的 需要一个全局自增的 key ps: 请不要使用 redlock 方案
WuMingyu
2019-10-14 13:50:31 +08:00
“就会遇到 A 实例拿到一个 token,B 实例把 token 更新了,就会导致 A 实例报错。”为啥会报错呢,本身新老 token 是可以共存一段时间的
Dganzh
2019-10-14 14:21:15 +08:00
@UserNameisNull
> 然后 B 也发现 token-1 过期,等待获取锁,得到锁,B 也更新了,就会导致重复更新。
B 不要去更新,因为没有直接获得锁说明已经有人获得了,这就说明已经有人去更新了,其他人只需静静等待 access_token 更新即可
xuanbg
2019-10-14 14:38:14 +08:00
@UserNameisNull 双检锁了解一下。B 获取到锁不是直接去更新,而是先检查一下 Token 是否可用。
676529483
2019-10-14 14:43:17 +08:00
赞同 5 楼,用锁会影响性能,只需要后台启一个进程,专门负责刷新 key,其他只负责取就行了
MrYELiex
2019-10-14 14:58:33 +08:00
分布式锁并不能解决这个问题 因为 token 并不止在 redis 中 微信那边也有
如果多个实例去刷新 token 那么后请求的会覆盖其他同时请求的实例刚生成的 token 这才是导致报错的原因

因此中控服务器维护这些会过期的秘钥是最好的选择
dot2017
2019-10-14 14:59:59 +08:00
本地缓存 key 没问题,需要补一个 version 键就行。拿的时候先判断 version 全局是否一致。每更新一次 key,version+1

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

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

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

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

© 2021 V2EX