之前在一篇文章中提到過训枢,因?yàn)闃I(yè)務(wù)的集群限流需求托修,在每次請求都需要拿到當(dāng)前的日期,不過精確到天即可恒界。上次給出的解決方案是睦刃,因?yàn)镃alendar的性能問題,選擇更加直接粗暴的方式十酣,就是下面這個(gè)涩拙。
private static final int DAY_MILLIS = 24 * 60 * 60 * 1000;
long day = System.currentTimeMillis() / DAY_MILLIS;
通過當(dāng)前的時(shí)間戳(毫秒級別),除以一天的毫秒數(shù)耸采,得到的結(jié)果就是從1970 到今天經(jīng)歷過的天數(shù)兴泥。
最近業(yè)務(wù)在使用限流功能時(shí),需要實(shí)現(xiàn)限制接口每天調(diào)用1w次虾宇。
只是搓彻,第一個(gè)版本的實(shí)現(xiàn)邏輯是這樣的,如果第一個(gè)請求是早上8點(diǎn)過來的,那么會在redis創(chuàng)建一個(gè)對應(yīng)的key好唯,并設(shè)置24小時(shí)過期竭沫。所以從今天早上8點(diǎn)到明天早上8點(diǎn),只能調(diào)用1w次骑篙,多余的就拒絕蜕提,所以每天限制的時(shí)間段可能是不一樣的。
業(yè)務(wù)提出了新需求:必須按照0點(diǎn)到第二天0的時(shí)間段進(jìn)行限制靶端。
對于這個(gè)需求谎势,我表示會心一笑,上述的粗暴方案不是正好滿足么杨名,三下五除二脏榆,就給加上了這個(gè)功能。
if (timeUnit == TimeUnit.DAYS) {
key = ruleId + TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis());
}
如果業(yè)務(wù)配置的限流周期是天台谍,那么就在原始id上再加上當(dāng)前的天數(shù)當(dāng)前redis的key须喂,那么每天的key都是不一樣的,新的一天趁蕊,都會有一個(gè)新的key坞生,如此的美好。
業(yè)務(wù)使用改進(jìn)后的版本上線了2天掷伙,甩過來一個(gè)監(jiān)控頁面是己,開始diss我們了,為什么昨天觸發(fā)了限流任柜,今天凌晨2點(diǎn)多還在限流卒废。看著埋點(diǎn)數(shù)據(jù)宙地,發(fā)現(xiàn)這個(gè)鍋是甩不掉了摔认,只能開始翻代碼,無果宅粥。
后來通過redis的埋點(diǎn)數(shù)據(jù)参袱,發(fā)現(xiàn)2點(diǎn)多操作的redis key上還是昨天的天數(shù)。很奇怪粹胯,有木有蓖柔?明明已經(jīng)2點(diǎn)了辰企,怎么還是昨天的天數(shù)风纠,我已經(jīng)開始懷疑服務(wù)器的時(shí)間是不是有問題,居然鬼使神差的讓業(yè)務(wù)去驗(yàn)證服務(wù)器的時(shí)間牢贸,結(jié)果是我被打臉了竹观。
到最后,我才把關(guān)注點(diǎn)放在了System.currentTimeMillis()
這個(gè)返回值上,這個(gè)返回的時(shí)間戳是從1970開始到現(xiàn)在格林威治時(shí)間的時(shí)間戳臭增,而北京時(shí)間比格林威治整整快了8個(gè)小時(shí)懂酱。
所以真相是,通過暴力方案返回的天數(shù)誊抛,在北京時(shí)間早上8點(diǎn)之前都算是之前一天列牺,過了8點(diǎn),才算是新的一天拗窃,才會使用新的redis key進(jìn)行計(jì)數(shù)瞎领。
繼續(xù)修復(fù),繼續(xù)發(fā)版随夸,下次在使用這種暴力方案時(shí)九默,考慮一下時(shí)區(qū)問題。