大家好,我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);//运营总规则去重(一天内用户收到最多同一个渠道的消息次数)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);}});}
我猜想他的思路就是把构建去重参数和选择具体的去重服务给封装起来了,在最外层的代码看起来就很简洁了。后来又跟他聊了下,他的设计思路是这样的:考虑到以后会有其他规则的去重就把去重逻辑单独封装起来了,之后用策略模版的设计模式进行了重构,重构后的代码 模版不变,支持各种不同策略的去重,扩展性更高更强更简洁
确实牛逼。
我基于上面的思路微改了下入口,代码最终演变成这样:
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阈值,还记得我曾经说过:「去重」的本质:「业务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);}}
前面提到的业务Key,是在AbstractDeduplicationService的子类下构建的:
而具体的去重逻辑实现则都在LimitService下,{一天内相同的用户如果已经收到某渠道内容5次}是在SimpleLimitService中处理使用mget和pipelineSetEX就完成了实现。而{5分钟内相同用户如果收到相同的内容}是在SlideWindowLimitService中处理,使用了lua脚本完成了实现。
LimitService的代码都来源于@caolongxiu的pull request,建议大家可以对比commit再学习一番:https://gitee.com/zhongfucheng/austin/pulls/19
1、频次去重采用普通的计数去重方法,限制的是每天发送的条数。
2、内容去重采用的是新开发的基于redis中zset的滑动窗口去重,可以做到严格控制单位时间内的频次。
3、redis使用lua脚本来保证原子性和减少网络io的损耗
4、redis的key增加前缀做到数据隔离(后期可能有动态更换去重方法的需求)
5、把具体限流去重方法从DeduplicationService抽取出来,DeduplicationService只需设置构造器注入时注入的AbstractLimitService(具体限流去重服务)类型即可动态更换去重的方法 6、使用雪花算法生成zset的唯一value,score使用的是当前的时间戳
针对滑动窗口去重,有会引申出新的问题:limit.lua的逻辑?为什么要移除时间窗口的之前的数据?为什么ARGV[4]参数要唯一?为什么要expire?
A: 使用滑动窗口可以保证N分钟达到N次进行去重。滑动窗口可以回顾下TCP的,也可以回顾下刷LeetCode时的一些题,那这为什么要移除,就不陌生了。
为什么ARGV[4]要唯一,具体可以看看zadd这条命令,我们只需要保证每次add进窗口内的成员是唯一的,那么就不会触发有更新的操作(我认为这样设计会更加简单些),而唯一Key用雪花算法比较方便。
为什么expire?,如果这个key只被调用一次。那就很有可能在redis内存常驻了,expire能避免这种情况。
推荐项目最后再叨叨吧,很多人可能会发一段截图,跑来问我为什么要这样写,为什么要以这种方式实现,能不能以这种方式实现。这时候,我更想看到的是:你已经实现了第二种方式了,然后探讨你写的这种方案好不好,现有的代码差在哪里。
毕竟问问题很简单,我又不是客服,总不能没诚意的问题我都得一一回答吧。
如果想学Java项目的,我还是强烈推荐我的开源项目消息推送平台Austin,可以用作毕业设计,可以用作校招,可以看看生产环境是怎么推送消息的。
仓库地址(可点击阅读原文跳转):https://gitee.com/zhongfucheng/austin
我开通了股东服务内容,感兴趣可以点击下方看看,主要针对的是项目哟
VIP服务
标签:
精彩推荐
早高峰期间如果有车横停在斑马线上堵住了双向车道你会选择按喇叭催促,还是等待?5月6日上午7点55分浙江慈
格隆汇5月9日丨邦彦技术688132SH公布2023年股票期权激励计划拟授予激励对象的股票期权数量为330万份约占本
抄写作文网小编为大家提供哪些古诗带有月带有月的古诗有那些来供大家参考,欢迎阅读。带有月的古诗有那些1
对于黄发垂髫的垂髫是什么意思这个问题感兴趣的朋友应该很多,这个也是目前大家比较关注的问题,那么下面小
✔北京市参保人员在津冀区域内就医,需要异地就医备案吗?答:不需要自2023年4月1日起,本市基本医疗保险参
5月7日-8日,2023西湖论剑·数字安全大会在杭州洲际酒店举办。安恒信息董事长范渊发表主题演讲。对于西...
5月9日上午,2023·湖南岳阳“茶绿樱花红·洞庭春”茶文化节在岳阳县黄沙街洞庭春茶场开幕。开幕仪式。...
据上海钢联发布数据显示,今日电池级碳酸锂涨6500元 吨,均价报21 25万元 吨,工业级碳酸锂涨8000元 吨,均
中视传媒涨停收盘,收盘价15 86元。该股于9点33分涨停,未打开涨停,截止收盘封单资金为8660 93万元,占其
提升全民数字素养与技能,对加快数字化发展、建设数字中国具有重大意义。以“数字赋能全民共享”为主题...
新华社金沙萨5月8日电(记者史彧)刚果(金)地方政府官员8日表示,该国东部南基伍省近日连降暴雨,造成的
1、展开3全部抖音上很火的特效视频可以通过美册软件来制作。2、工具 原料:手机:魅族PRO7。3、手机系统版本号
NBA季后赛西部半决赛G4,湖人主场104-101击败勇士,大比分3-1领先。本场比赛,朗尼-沃克以替补身份在末节得
该公司拥有一支专业的团队,经验丰富,技术过硬,能够为拼多多店铺主提供全方位的代运营服务。无论是店铺开
金昌启动未来火星生活品牌共创计划,火星生活,航天,金昌,航天员,戈壁,火星
近日,全区科学技术奖励大会在银川召开,表彰奖励了2021年度全区科技创新成果,全区共有5项科技创新成果获
5月8日,由农业农村部市场与信息化司指导、中国农业大学主办的2023中国农业品牌创新发展大会在北京召开。第
5月9日,记者从山西吕梁汾阳市政府新闻办获悉,5月8日晚,汾阳市一居民小区住宅楼发生火灾,致3人死亡。经
为强化后备战力,国军今天首度办理女性后备教召。首梯14位女性受召人员在今午12时前,至桃园八德大湳森林公
5月6日,首届“510中国品牌日·能源产业品牌宣传周”(以下简称“能源品牌宣传周”)活动在京启动,主题为...
万圣节前夜并不是以吃火鸡为主要的节日活动,而是以“南瓜灯”、“化装派对”、“糖果游戏”等为主要活...
加拿大模特在中国坐月子,视频火了到了国外!5月7日报道,加拿大女子泰勒·理查德在视频网站优兔发布自...
路易•艾黎于1944年来到山丹,筹建培黎学校,兴办工合企业,在这片土地上工作生活了近十年,将山丹视为自己
对方老婆发我不雅照片可以怎么办
5月8日,中国银行间市场交易商协会(下称“交易商协会”)发布消息称,近期,交易商协会通过深入调查发...
资讯News
08-28
06-20
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
聚焦Policy
当好农民工的“护薪人” 近日,罗某等7名农民工在收到被拖欠的工资后,纷纷打电话向江西省南昌市...
“通讯录里所有人都知道我欠钱了” □ 本报记者 韩丹东 □ 本报见习记者 张守坤 ...
大连宝马车撞人案肇事司机被判死刑 本报讯 记者韩宇 10月29日,辽宁省大连市中级人民法院一审...
医院财务迷上网络赌博输光5000万元公款 □ 本报记者 马维博 □ 本报通讯员 汪宇堂 曹...
辊环车削 雕琢毫厘(工匠绝活) 【绝活看点】 23年来,雷虎始终扎根一线,改进钢材轧制工艺...
交警严查超标电动自行车挪用“白牌” 截至昨晚6时,处罚电动自行车违法行为共计6585笔;下一步将...
明起寒潮来袭 北方气温普降10℃以上 中央气象台预计,本周日北京平原地区最低气温降至-4℃左右...
多种蔬菜价格降幅达五成 包括菠菜、蒿子秆等 预计本月中旬蔬菜恢复供需平衡 本报讯(记者...
北京周日最低气温或达-4℃ 本报讯(记者 赵婷婷)北京青年报记者昨天从中央气象台获悉,新一股...
昌平一家四口确诊新冠肺炎 天通北苑第二社区升级为中风险地区 朝阳两涉疫校区及16所学校停课 ...