大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

分布式限流的一次坑

之前在一篇文章中提到过,因为业务的集群限流需求,在每次请求都需要拿到当前的日期,不过精确到天即可。上次给出的解决方案是,因为Calendar的性能问题,选择更加直接粗暴的方式,就是下面这个。


通过当前的时间戳(毫秒级别),除以一天的毫秒数,得到的结果就是从1970 到今天经历过的天数。

最近业务在使用限流功能时,需要限制接口每天调用1w次。

不过,在限流实现中,第一个版本的实现逻辑是这样的,如果第一个请求是早上8点过来的,那么会在redis创建一个对应的key,并设置24小时过期,所以从今天早上8点到明天早上8点,只能调用1w次,多余的就拒绝。但是有个问题,每天限制的时间段可能是不一样的。

业务提出了新需求:必须按照0点到第二天0的时间段进行限制。

对于这个需求,我表示会心一笑,上述的粗暴方案不是正好满足么,三下五除二,就给加上了这个功能。


如果业务配置的限流周期是天,那么就在原始id上再加上当前的天数当前redis的key,那么每天的key都是不一样的,新的一天,都会有一个新的key,如此的美好。

业务使用改进后的版本上线了2天,甩过来一个监控页面,开始diss我们了,为什么昨天触发了限流,今天凌晨2点多还在限流。看着埋点数据,发现这个锅是甩不掉了,只能开始翻代码,无果。

后来通过redis的埋点数据,发现凌晨2点多操作的redis key还是带着昨天的天数。这个就很奇怪了?明明已经第二天2点了,怎么还是昨天的天数,这时候我已经开始怀疑服务器的时间是不是有问题,鬼使神差的让业务同学去验证,结果肯定是被打脸了。

排查了很久,才把关注点放在了 System.currentTimeMillis()这个方法的返回值上,其实返回的时间戳是从1970开始到现在格林威治时间的时间戳,而北京时间比格林威治整整快了8个小时。

所以真相是,通过暴力方案返回的天数,在北京时间早上8点之前都算是之前一天,过了8点,才算是新的一天,才会使用新的redis key进行计数,感觉自己埋下的坑,有下面这么大!

继续修复,继续发版,下次在使用这种暴力方案时,考虑一下时区问题。