解剖屎山,尋覓黃金之第二彈|天天訊息
大家好,我3y啊。由于去重邏輯重構了幾次,好多股東直呼看不懂,于是我今天再安排一波對代碼的解析吧。austin支持兩種去重的類型:N分鐘相同內容達到N次去重和一天內N次相同渠道頻次去重。
在最開始,我的第一版實現是這樣的:
publicvoidduplication(TaskInfotaskInfo){//配置示例:{"contentDeduplication":{"num":1,"time":300},"frequencyDeduplication":{"num":5}}JSONObjectproperty=JSON.parseObject(config.getProperty(DEDUPLICATION_RULE_KEY,AustinConstant.APOLLO_DEFAULT_VALUE_JSON_OBJECT));JSONObjectcontentDeduplication=property.getJSONObject(CONTENT_DEDUPLICATION);JSONObjectfrequencyDeduplication=property.getJSONObject(FREQUENCY_DEDUPLICATION);//文案去重DeduplicationParamcontentParams=DeduplicationParam.builder().deduplicationTime(contentDeduplication.getLong(TIME)).countNum(contentDeduplication.getInteger(NUM)).taskInfo(taskInfo).anchorState(AnchorState.CONTENT_DEDUPLICATION).build();contentDeduplicationService.deduplication(contentParams);//運營總規(guī)則去重(一天內用戶收到最多同一個渠道的消息次數)Longseconds=(DateUtil.endOfDay(newDate()).getTime()-DateUtil.current())/1000;DeduplicationParambusinessParams=DeduplicationParam.builder().deduplicationTime(seconds).countNum(frequencyDeduplication.getInteger(NUM)).taskInfo(taskInfo).anchorState(AnchorState.RULE_DEDUPLICATION).build();frequencyDeduplicationService.deduplication(businessParams);}
(相關資料圖)
那時候很簡單,基本主體邏輯都寫在這個入口上了,應該都能看得懂。后來,群里滴滴哥表示這種代碼不行,不能一眼看出來它干了什么。于是怒提了一波pull request重構了一版,入口是這樣的:
publicvoidduplication(TaskInfotaskInfo){//配置樣例:{"contentDeduplication":{"num":1,"time":300},"frequencyDeduplication":{"num":5}}Stringdeduplication=config.getProperty(DeduplicationConstants.DEDUPLICATION_RULE_KEY,AustinConstant.APOLLO_DEFAULT_VALUE_JSON_OBJECT);//去重DEDUPLICATION_LIST.forEach(key->{DeduplicationParamdeduplicationParam=builderFactory.select(key).build(deduplication,key);if(deduplicationParam!=null){deduplicationParam.setTaskInfo(taskInfo);DeduplicationServicededuplicationService=findService(key+SERVICE);deduplicationService.deduplication(deduplicationParam);}});}
我猜想他的思路就是把構建去重參數和選擇具體的去重服務給封裝起來了,在最外層的代碼看起來就很簡潔了。后來又跟他聊了下,他的設計思路是這樣的:考慮到以后會有其他規(guī)則的去重就把去重邏輯單獨封裝起來了,之后用策略模版的設計模式進行了重構,重構后的代碼 模版不變,支持各種不同策略的去重,擴展性更高更強更簡潔
確實牛逼。
我基于上面的思路微改了下入口,代碼最終演變成這樣:
publicvoidduplication(TaskInfotaskInfo){//配置樣例:{"deduplication_10":{"num":1,"time":300},"deduplication_20":{"num":5}}StringdeduplicationConfig=config.getProperty(DEDUPLICATION_RULE_KEY,CommonConstant.EMPTY_JSON_OBJECT);//去重ListdeduplicationList=DeduplicationType.getDeduplicationList();for(IntegerdeduplicationType:deduplicationList){DeduplicationParamdeduplicationParam=deduplicationHolder.selectBuilder(deduplicationType).build(deduplicationConfig,taskInfo);if(Objects.nonNull(deduplicationParam)){deduplicationHolder.selectService(deduplicationType).deduplication(deduplicationParam);}}}
到這,應該大多數人還能跟上吧?在講具體的代碼之前,我們先來簡單看看去重功能的代碼結構(這會對后面看代碼有幫助)
去重的邏輯可以統一抽象為:在X時間段內達到了Y閾值,還記得我曾經說過:「去重」的本質:「業(yè)務Key」+「存儲」。那么去重實現的步驟可以簡單分為(我這邊存儲就用的Redis):
通過Key從Redis獲取記錄判斷該Key在Redis的記錄是否符合條件符合條件的則去重,不符合條件的則重新塞進Redis更新記錄為了方便調整去重的參數,我把X時間段和Y閾值都放到了配置里{"deduplication_10":{"num":1,"time":300},"deduplication_20":{"num":5}}。目前有兩種去重的具體實現:
1、5分鐘內相同用戶如果收到相同的內容,則應該被過濾掉
2、一天內相同的用戶如果已經收到某渠道內容5次,則應該被過濾掉
從配置中心拿到配置信息了以后,Builder就是根據這兩種類型去構建出DeduplicationParam,就是以下代碼:
DeduplicationParamdeduplicationParam=deduplicationHolder.selectBuilder(deduplicationType).build(deduplicationConfig,taskInfo);
Builder和DeduplicationService都用了類似的寫法(在子類初始化的時候指定類型,在父類統一接收,放到Map里管理)
而統一管理著這些服務有個中心的地方,我把這取名為DeduplicationHolder
/***@authorhuskey*@date2022/1/18*/@ServicepublicclassDeduplicationHolder{privatefinalMapbuilderHolder=newHashMap<>(4);privatefinalMap serviceHolder=newHashMap<>(4);publicBuilderselectBuilder(Integerkey){returnbuilderHolder.get(key);}publicDeduplicationServiceselectService(Integerkey){returnserviceHolder.get(key);}publicvoidputBuilder(Integerkey,Builderbuilder){builderHolder.put(key,builder);}publicvoidputService(Integerkey,DeduplicationServiceservice){serviceHolder.put(key,service);}}
前面提到的業(yè)務Key,是在AbstractDeduplicationService的子類下構建的:
而具體的去重邏輯實現則都在LimitService下,{一天內相同的用戶如果已經收到某渠道內容5次}是在SimpleLimitService中處理使用mget和pipelineSetEX就完成了實現。而{5分鐘內相同用戶如果收到相同的內容}是在SlideWindowLimitService中處理,使用了lua腳本完成了實現。
LimitService的代碼都來源于@caolongxiu的pull request,建議大家可以對比commit再學習一番:https://gitee.com/zhongfucheng/austin/pulls/19
1、頻次去重采用普通的計數去重方法,限制的是每天發(fā)送的條數。
2、內容去重采用的是新開發(fā)的基于redis中zset的滑動窗口去重,可以做到嚴格控制單位時間內的頻次。
3、redis使用lua腳本來保證原子性和減少網絡io的損耗
4、redis的key增加前綴做到數據隔離(后期可能有動態(tài)更換去重方法的需求)
5、把具體限流去重方法從DeduplicationService抽取出來,DeduplicationService只需設置構造器注入時注入的AbstractLimitService(具體限流去重服務)類型即可動態(tài)更換去重的方法 6、使用雪花算法生成zset的唯一value,score使用的是當前的時間戳
針對滑動窗口去重,有會引申出新的問題:limit.lua的邏輯?為什么要移除時間窗口的之前的數據?為什么ARGV[4]參數要唯一?為什么要expire?
A: 使用滑動窗口可以保證N分鐘達到N次進行去重?;瑒哟翱诳梢曰仡櫹耇CP的,也可以回顧下刷LeetCode時的一些題,那這為什么要移除,就不陌生了。
為什么ARGV[4]要唯一,具體可以看看zadd這條命令,我們只需要保證每次add進窗口內的成員是唯一的,那么就不會觸發(fā)有更新的操作(我認為這樣設計會更加簡單些),而唯一Key用雪花算法比較方便。
為什么expire?,如果這個key只被調用一次。那就很有可能在redis內存常駐了,expire能避免這種情況。
推薦項目最后再叨叨吧,很多人可能會發(fā)一段截圖,跑來問我為什么要這樣寫,為什么要以這種方式實現,能不能以這種方式實現。這時候,我更想看到的是:你已經實現了第二種方式了,然后探討你寫的這種方案好不好,現有的代碼差在哪里。
畢竟問問題很簡單,我又不是客服,總不能沒誠意的問題我都得一一回答吧。
如果想學Java項目的,我還是強烈推薦我的開源項目消息推送平臺Austin,可以用作畢業(yè)設計,可以用作校招,可以看看生產環(huán)境是怎么推送消息的。
倉庫地址(可點擊閱讀原文跳轉):https://gitee.com/zhongfucheng/austin
我開通了股東服務內容,感興趣可以點擊下方看看,主要針對的是項目喲
VIP服務
關鍵詞:
- 解剖屎山,尋覓黃金之第二彈|天天訊息(2023-05-29 12:24:30)
- 天天報道:期貨公司觀點匯總一張圖:5月29日有色系(銅、鋅、鋁、鎳、錫等)(2023-05-29 12:13:39)
- 當前熱門:期貨公司觀點匯總一張圖:5月29日農產品(棉花、豆粕、白糖、玉米、雞蛋、生豬等)(2023-05-29 12:14:08)
- 長安期貨5月29日貴金屬日報:美國4月PCE數據超預期疊加美債危機緩解,貴金屬或震蕩偏弱 環(huán)球關注(2023-05-29 12:21:16)
- 全球領先,銀聯的這一市場份額超VISA 當前快訊(2023-05-29 11:46:55)
- 坑完粉絲 3000 萬,財經大佬去吃牢飯了(2023-05-29 12:01:19)
- 工行連線:美國通脹再超預期 金價徘徊于8周來低位-全球報資訊(2023-05-29 11:58:21)
- 《富爸爸,窮爸爸》:5節(jié)財商課,拉近你和富人之間的距離_環(huán)球要聞(2023-05-29 11:50:07)
- 天天通訊!【圖片新聞】津南區(qū)雙新街萬盈家園社區(qū)小青團自習室開講啦!(2023-05-29 11:55:41)
- 全球時訊:諾唯贊:NGS賽道領軍者 細分領域大展拳腳(2023-05-29 11:58:24)
- 首談AI失控:李彥宏說要先上牌桌,才能擁有話語權(2023-05-29 11:53:30)
- 【全球速看料】400只蚊子實測12款驅蚊產品:驅蚊手環(huán)、驅蚊貼、滅蚊燈、驅蚊APP效果堪憂(2023-05-29 11:44:30)
- 資訊推薦:玄麥甘桔顆粒適合治什么?。ㄐ湼式垲w粒)(2023-05-29 11:34:46)
- 金輝集團13.5億公司債“21金輝01”后兩年利率仍為6.95%|全球快看點(2023-05-29 11:15:16)
- 中信建投期貨5月29日早間交易策略(2023-05-29 11:11:01)
- 5月29日匯市觀潮:歐元、英鎊和澳元技術分析(2023-05-29 11:17:20)
- 5月29日機構對金融市場觀點匯總 焦點信息(2023-05-29 11:19:40)
- 天天快消息!涓誨娑插寲澶╃劧姘斾笟鐨勭珵浜夋縺鐑堬紝姝e艦鎴愪竴涓拱鏂瑰競鍦猴紒(2023-05-29 11:00:43)
- 環(huán)球速讀:繼特斯拉、蔚小理后,比亞迪36億收購財險公司自營車險,圍獵人太平(2023-05-29 11:06:09)
- 諾亞財富:穿透投資周期迷霧,探索財富管理行業(yè)國際化經驗(2023-05-29 10:48:01)
- 中國農業(yè)銀行:將對個人長期不動存折賬戶開展排查清理工作 全球通訊(2023-05-29 10:49:22)
- 視頻丨理財產品紛紛下調業(yè)績基準,各類產品收益全線下行,為什么,怎么辦?(2023-05-29 10:42:27)
- 每日觀點:普通人擺脫平庸的辦法,80%的人都搞錯了(2023-05-29 10:53:06)
- 天天熱推薦:【讀財報】三年期互聯網主題基金透視:天弘基金業(yè)績墊底,長盛基金風格較激進(2023-05-29 10:42:12)
- 神秘公司成立17天,便豪擲1.2億搶下北京頂級豪宅(2023-05-29 10:51:45)
- 50億大減持突襲組件龍頭,天合光能暴跌超16%,光伏ETF基金(516290)大跌近3%-天天看熱訊(2023-05-29 10:55:03)
- 直播帶貨這塊“蛋糕”,AIGC也想分一口(2023-05-29 10:37:31)
- 天天熱點評!《家有兒女》疑似被惡意打分具體是什么情況(2023-05-29 10:43:26)
- 堆料不如堆科技!華為P60系列口碑、銷量遙遙領先(2023-05-29 10:22:24)
- 三立期貨5月29日早間內參——能化(2023-05-29 10:01:03)
-
資訊推薦:玄麥甘桔顆粒適合治什么病(玄麥
1、玄麥甘桔顆粒的主要成分是甘草酸,可以幫助我們清熱滋陰,同時還有祛痰的功效。2、...
-
金輝集團13.5億公司債“21金輝01”后兩年利
“21金輝01”發(fā)行總額13 5億元,期限5年,附第2年末和第4年末發(fā)行人調整票面利率選擇...
-
中信建投期貨5月29日早間交易策略
全球原油終端需求進入平穩(wěn)期更進一步的增長需要時間當下中國與歐美等國家航班的復飛受...
-
5月29日匯市觀潮:歐元、英鎊和澳元技術分析
貨幣歐元美元阻力位210900阻力位110830即期價格10719支持位110700支持位210660上周五5...
-
5月29日機構對金融市場觀點匯總 焦點信息
5月29日機構對股市大宗商品外匯債市經濟前景以及央行政策前景觀點匯總1IMF敦促美國立即...
-
天天快消息!涓誨娑插寲澶╃劧姘斾笟鐨勭
璁€斺€?鏈?8鏃?甯傚満鍒嗘瀽甯圚aleyZaremba縐?涓誨娑插寲澶╃劧姘旇涓氱殑绔...
-
環(huán)球速讀:繼特斯拉、蔚小理后,比亞迪36億
作者|Gary來源|汽車服務世界(ID:asworld168)比亞迪已經確定入局車險了。日前,國家...
-
諾亞財富:穿透投資周期迷霧,探索財富管理
南方財經全媒體記者翁榕濤實習生林婉娜廣州報道“從依賴資源稟賦的高速增長到強調去杠...
-
中國農業(yè)銀行:將對個人長期不動存折賬戶開
尊敬的客戶:為有效防范電信網絡新型違法犯罪,切實保障您的賬戶與資金安全和合法權益...
-
視頻丨理財產品紛紛下調業(yè)績基準,各類產品
出品丨21世紀經濟報道21世紀資管研究院統籌丨方海平內容丨唐曜華配音|羅晶晶視頻丨張迎...
- 資訊推薦:玄麥甘桔顆
- 金輝集團13.5億公司債
- 堆料不如堆科技!華為
- 世界視訊!應對新冠病
- 資訊推薦:玄麥甘桔顆粒適合治什么?。ㄐ湼式?/a>
- 金輝集團13.5億公司債“21金輝01”后兩年利率仍
- 堆料不如堆科技!華為P60系列口碑、銷量遙遙領先
- 世界視訊!應對新冠病毒,請繼續(xù)保持良好衛(wèi)生習
- 魯北化工:公司將加快推進建設項目達產達效,進
- 天天看熱訊:純電續(xù)航更適中 騰勢D9 DM-i兩驅
- 環(huán)球動態(tài):實驗室制取氫氣方程式現象_實驗室制取
- 現實版《失孤》在這趟飛馳的列車上上演!
- 當前熱議!儲蓄的方式分為哪幾種?暑假作業(yè)_儲蓄
- 世界關注:香港it品牌_香港it
- 滾動
- 綜合
- 房產