最新要闻
- 全球新消息丨《最终幻想16》仍然只有白人:没有对人种多样性妥协
- 全球聚焦:AMD Zen4正式登顶!16核7954HX性能战平24核13980HX、功耗低得多
- 老司机全程不踩刹车?特斯拉潮州事故车主不服鉴定:官方尚未出责任认定书
- 天天百事通!3888元 + 可叠加百亿补贴:天猫无门槛红包12点正式开抢
- 4条狼青犬咬死几十只羊!警惕:性情凶狠、攻击性非常强
- 全球微头条丨211文科硕士吐槽均薪5500引争议:文科生转码或成趋势 还是理科香?
- 报道:“RNG老板道歉”登热搜 CEO:轮换中单是我的决定
- 【世界播资讯】李想:理想汽车要占20万元以上市场35% 将对标苹果特斯拉
- 垂头丧气的丧是什么意思?关于垂头丧气的反义词有哪些?
- 父亲的兄弟如何称呼?写给父亲的一封信作文模板
- 张靓颖的海豚音是哪首歌?张靓颖终于等到你的歌词是什么?
- 李想:如果不卖电池 电动车成本可以比燃油车低
- 热议:马斯克最疯计划曝光!给我10万亿美元:可拯救地球
- 梅西花百万买苹果手机送给阿根廷队友:定制了35部24K金的iPhone 14 Pro
- 天天微头条丨河南小伙1:1打造歼10战斗机模型!司机:这辈子拉过最硬的货
- 每日简讯:奇瑞背刺长城 捷途旅行者实车亮相:10多万的“硬汉”登场
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
世界简讯:风控系统就该这么设计,万能通用,稳的一批!(建议收藏)
作者:wingli链接:https://juejin.cn/post/7182774381448282172
一、背景
1.为什么要做风控?
这不得拜产品大佬所赐
(相关资料图)
目前我们业务有使用到非常多的AI能力,如ocr识别、语音测评等,这些能力往往都比较费钱或者费资源,所以在产品层面也希望我们对用户的能力使用次数做一定的限制,因此风控是必须的!
2.为什么要自己写风控?
那么多开源的风控组件,为什么还要写呢?是不是想重复发明轮子呀. 要想回答这个问题,需要先解释下我们业务需要用到的风控(简称业务风控),与开源常见的风控(简称普通风控)有何区别:
风控类型 | 目的 | 对象 | 规则 |
---|---|---|---|
业务风控 | 实现产品定义的一些限制,达到限制时,有具体的业务流程,如充值vip等 | 比较复杂多变的,例如针对用户进行风控,也能针对用户+年级进行风控 | 自然日、自然小时等 |
普通风控 | 保护服务或数据,拦截异常请求等 | 接口、部分可以加上简单参数 | 一般用得更多的是滑动窗口 |
因此,直接使用开源的普通风控,一般情况下是无法满足需求的
3.其它要求
支持实时调整限制:
很多限制值在首次设置的时候,基本上都是拍定的一个值,后续需要调整的可能性是比较大的,因此可调整并实时生效是必须的
二、思路
要实现一个简单的业务风控组件,要做什么工作呢?
1.风控规则的实现
a.需要实现的规则:
- 自然日计数
- 自然小时计数
- 自然日+自然小时计数
自然日+自然小时计数 这里并不能单纯地串联两个判断,因为如果自然日的判定通过,而自然小时的判定不通过的时候,需要回退,自然日跟自然小时都不能计入本次调用!
b.计数方式的选择:
目前能想到的会有:
- mysql+db事务持久化、记录可溯源、实现起来比较麻烦,稍微“重”了一点
- redis+lua实现简单,redis的可执行lua脚本的特性也能满足对“事务”的要求
- mysql/redis+分布式事务需要上锁,实现复杂,能做到比较精确的计数,也就是真正等到代码块执行成功之后,再去操作计数
目前没有很精确技术的要求,代价太大,也没有持久化的需求,因此选用
redis+lua
即可
2.调用方式的实现
a.常见的做法
先定义一个通用的入口
推荐一个开源免费的 Spring Boot 最全教程:
https://github.com/javastacks/spring-boot-best-practice
//简化版代码@Componentclass DetectManager { fun matchExceptionally(eventId: String, content: String){ //调用规则匹配 val rt = ruleService.match(eventId,content) if (!rt) { throw BaseException(ErrorCode.OPERATION_TOO_FREQUENT) } }}
在service中调用该方法
//简化版代码@Serviceclass OcrServiceImpl : OcrService { @Autowired private lateinit var detectManager: DetectManager /** * 提交ocr任务 * 需要根据用户id来做次数限制 */ override fun submitOcrTask(userId: String, imageUrl: String): String { detectManager.matchExceptionally("ocr", userId) //do ocr }}
有没有更优雅一点的方法呢? 用注解可能会更好一点(也比较有争议其实,这边先支持实现)
由于传入的 content 是跟业务关联的,所以需要通过Spel来将参数构成对应的content
三、具体实现
1.风控计数规则实现
a.自然日/自然小时
自然日/自然小时可以共用一套lua
脚本,因为它们只有key
不同,脚本如下:
//lua脚本local currentValue = redis.call("get", KEYS[1]);if currentValue ~= false then if tonumber(currentValue) < tonumber(ARGV[1]) then return redis.call("INCR", KEYS[1]); else return tonumber(currentValue) + 1; end;else redis.call("set", KEYS[1], 1, "px", ARGV[2]); return 1;end;
其中 KEYS[1]
是日/小时关联的key
,ARGV[1]
是上限值,ARGV[2]
是过期时间,返回值则是当前计数值+1后的结果,(如果已经达到上限,则实际上不会计数)
b.自然日+自然小时
如前文提到的,两个的结合实际上并不是单纯的拼凑,需要处理回退逻辑
//lua脚本local dayValue = 0;local hourValue = 0;local dayPass = true;local hourPass = true;local dayCurrentValue = redis.call("get", KEYS[1]);if dayCurrentValue ~= false then if tonumber(dayCurrentValue) < tonumber(ARGV[1]) then dayValue = redis.call("INCR", KEYS[1]); else dayPass = false; dayValue = tonumber(dayCurrentValue) + 1; end;else redis.call("set", KEYS[1], 1, "px", ARGV[3]); dayValue = 1;end;local hourCurrentValue = redis.call("get", KEYS[2]);if hourCurrentValue ~= false then if tonumber(hourCurrentValue) < tonumber(ARGV[2]) then hourValue = redis.call("INCR", KEYS[2]); else hourPass = false; hourValue = tonumber(hourCurrentValue) + 1; end;else redis.call("set", KEYS[2], 1, "px", ARGV[4]); hourValue = 1;end;if (not dayPass) and hourPass then hourValue = redis.call("DECR", KEYS[2]);end;if dayPass and (not hourPass) then dayValue = redis.call("DECR", KEYS[1]);end;local pair = {};pair[1] = dayValue;pair[2] = hourValue;return pair;
其中 KEYS[1]
是天关联生成的key
, KEYS[2]
是小时关联生成的key
,ARGV[1]
是天的上限值,ARGV[2]
是小时的上限值,ARGV[3]
是天的过期时间,ARGV[4]
是小时的过期时间,返回值同上
这里给的是比较粗糙的写法,主要需要表达的就是,进行两个条件判断时,有其中一个不满足,另一个都需要进行回退.
2.注解的实现
a.定义一个@Detect
注解
@Retention(AnnotationRetention.RUNTIME)@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)annotation class Detect( /** * 事件id */ val eventId: String = "", /** * content的表达式 */ val contentSpel: String = "")
其中content
是需要经过表达式解析出来的,所以接受的是个String
b.定义@Detect
注解的处理类
@Aspect@Componentclass DetectHandler { private val logger = LoggerFactory.getLogger(javaClass) @Autowired private lateinit var detectManager: DetectManager @Resource(name = "detectSpelExpressionParser") private lateinit var spelExpressionParser: SpelExpressionParser @Bean(name = ["detectSpelExpressionParser"]) fun detectSpelExpressionParser(): SpelExpressionParser { return SpelExpressionParser() } @Around(value = "@annotation(detect)") fun operatorAnnotation(joinPoint: ProceedingJoinPoint, detect: Detect): Any? { if (detect.eventId.isBlank() || detect.contentSpel.isBlank()){ throw illegalArgumentExp("@Detect config is not available!") } //转换表达式 val expression = spelExpressionParser.parseExpression(detect.contentSpel) val argMap = joinPoint.args.mapIndexed { index, any -> "arg${index+1}" to any }.toMap() //构建上下文 val context = StandardEvaluationContext().apply { if (argMap.isNotEmpty()) this.setVariables(argMap) } //拿到结果 val content = expression.getValue(context) detectManager.matchExceptionally(detect.eventId, content) return joinPoint.proceed() }}
需要将参数放入到上下文中,并起名为arg1
、arg2
....
四、测试一下
1.写法
使用注解之后的写法:
//简化版代码@Serviceclass OcrServiceImpl : OcrService { @Autowired private lateinit var detectManager: DetectManager /** * 提交ocr任务 * 需要根据用户id来做次数限制 */ @Detect(eventId = "ocr", contentSpel = "#arg1") override fun submitOcrTask(userId: String, imageUrl: String): String { //do ocr }}
2.Debug看看
- 注解值获取成功
- 表达式解析成功
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!
-
世界简讯:风控系统就该这么设计,万能通用,稳的一批!(建议收藏)
作者:wingli链接:https: juejin cn post 7182774381448282172一、背景1 为什么要做风控?这不得...
来源: 宕机了,Redis 如何避免数据丢失?
世界简讯:风控系统就该这么设计,万能通用,稳的一批!(建议收藏)
焦点要闻:权限提升(1)
全球新消息丨《最终幻想16》仍然只有白人:没有对人种多样性妥协
每日报道:sonar代码扫描bug:Use try-with-resources or close this "FileInputStream" in a "f
世界时讯:通过手动创建hibernate工厂,自动生成表,完成数据库备份还原功能
每日简讯:【36oj】 画圣诞树
全球聚焦:AMD Zen4正式登顶!16核7954HX性能战平24核13980HX、功耗低得多
老司机全程不踩刹车?特斯拉潮州事故车主不服鉴定:官方尚未出责任认定书
【播资讯】不为人知的网络编程(十五):深入操作系统,一文搞懂Socket到底是什么
轻松玩转makefile | 变量与模式
天天百事通!3888元 + 可叠加百亿补贴:天猫无门槛红包12点正式开抢
4条狼青犬咬死几十只羊!警惕:性情凶狠、攻击性非常强
全球微头条丨211文科硕士吐槽均薪5500引争议:文科生转码或成趋势 还是理科香?
报道:“RNG老板道歉”登热搜 CEO:轮换中单是我的决定
【世界播资讯】李想:理想汽车要占20万元以上市场35% 将对标苹果特斯拉
垂头丧气的丧是什么意思?关于垂头丧气的反义词有哪些?
头条焦点:百分比堆叠柱状图适用情形有哪些?速戳!
父亲的兄弟如何称呼?写给父亲的一封信作文模板
张靓颖的海豚音是哪首歌?张靓颖终于等到你的歌词是什么?
李想:如果不卖电池 电动车成本可以比燃油车低
热议:马斯克最疯计划曝光!给我10万亿美元:可拯救地球
梅西花百万买苹果手机送给阿根廷队友:定制了35部24K金的iPhone 14 Pro
天天微头条丨河南小伙1:1打造歼10战斗机模型!司机:这辈子拉过最硬的货
每日简讯:奇瑞背刺长城 捷途旅行者实车亮相:10多万的“硬汉”登场
早晨问候客户的正能量句子有哪些?早晨问候语有哪些?
仓央嘉措是哪个朝代的?仓央嘉措经典诗句有哪些?
蓝码健康码是什么意思?蓝码健康码是正常的吗?
32开纸是多大?32开纸有多大是几个A4?
ps字体怎么加描边?ps字体太小怎么调大?
移动硬盘参数怎么看?移动硬盘参数错误怎么解决?
usd是什么意思?usdt属于什么币种?
全球快讯:带有雨的诗句有哪些_带有雨的诗句具体有哪些
环球观焦点:WebLogic JNDI注入(CVE-2021-2109)
天天通讯!Java 根据模板导出PDF
从菜鸟程序员到高级架构师,竟然是因为这个字final
剑指 Offer 64. 求 1 + 2 + … + n(java解题)
当前简讯:浙江女子1600公里追到广州找到被偷的爱猫:苹果AirTag定位器立了大功
比亚迪豪华MPV成了!腾势D9上月热销7325台:均价41.5万
简讯:俞敏洪最新演讲:不喜欢《狂飙》 企业家只想赚钱就会像高启强后患无穷
复旦MOSS团队:取名是致敬《流浪地球2》 参数规模约ChatGPT的1/10
世界热推荐:跑着跑着会熄火 日产北美召回超80万辆奇骏:车钥匙背锅
环球头条:Git介绍下载安装以及基本使用
全球新消息丨解释器模式
每日时讯!promethues【centos7】时间同步
What is Point ?
【全球新要闻】那舅特大桥建成 又一时速350高铁开铺 南宁至玉林仅50分钟
3899元起 惠普战66六代锐龙版上架:锐龙7000系列加持
世界热议:3月17日开启Beta测试!《暗黑破坏神4》新预告片透露更多游戏内容
焦点短讯!拳头《无畏契约》3月14日起不再支持Win7/8/8.1系统:为了打击外挂!
论文阅读笔记(四):AS-MLP AN AXIAL SHIFTED MLP ARCHITECTUREFOR VISION
(数据库系统概论|王珊)第七章数据库设计-第五、六节:物理结构设计和数据库的实施和维护
全球消息!苹果何时大降价?iPhone 14 Plus成系列销量最差:用户宁愿买安卓
焦点信息:寓言诚不欺我!网友拍下现实版“乌鸦喝水”
焦点报道:儿子篮球班倒闭家长花1000万买下 网友:这就是钞能力
全球热议:999元卷王小金刚!优派推出VX2758显示器:27英寸2K/170Hz
15万就能买特斯拉?特斯拉宣布重大目标:成本降低50%
哪吒汽车2月份交付10073台 同比大涨41.5%
未成年人沉迷短视频得治 TikTok默认限制每天可刷一小时
“自己造自己” 特斯拉人形机器人亮相!马斯克承认罕见事实
【世界速看料】腾讯新游《黎明觉醒:生机》开放60帧:骁龙888、iPhone 13以上都能开
每日视点!印度男子展示绝技“乌鸦召唤术” 网友:在古代至少巫师级别
读Java性能权威指南(第2版)笔记06_数据库性能JPA&SpringData
全省严查!正在进行!
每日看点!马斯克大力推荐!特斯拉Cybertruck实车亮相:超级未来感
天天消息!马斯克宏图计划公布:储能240TWh 制造投资10万亿美元
日本死亡人数是新生儿数量两倍有多可怕:850万“幽灵屋”遍布全国
信息:特斯拉下一代电机将不需要任何稀土成分!马斯克挑战全球车企
环球新动态:Spark系列 - (5) Spark Shuffle
热消息:Fireasy3 揭秘 -- 万物伊始(依赖注入与服务发现)
全球信息:英语四级阅读技巧
一加Ace 2V 12+256G起步行业罕见:友商还在搞8+128卡价位的版本
实时:Redmi Note 12 Pro极速版12+256G到手1999元:开机就是MIUI 14
造车新势力2月交付量出炉:理想、蔚来、哪吒破万 零跑压力大
【全球速看料】厦门征求意见!过马路玩手机或将罚款50元 你支持吗?
世界快看:东风概念飞行汽车外观曝光!“鸥翼门”相当炫酷
【当前热闻】2018巴彦淖尔国际马拉松
环球焦点!胡明轩:平时杜导叫我和徐杰一起训练 要求我们承担起更多责任
世界快讯:makefile
基于alpine基础镜像构建jdk镜像以及tomcat镜像及业务构建
Linux极简入门系列(六):其它补充
CSS全局关键字
环球聚焦:委员建议隔周三休成热搜第一 网友吵翻 专家:很难行得通
今日热闻!Model 2明天发?这款15万的特斯拉便宜车:马斯克已经说了17年
环球新消息丨LOJ 3276 JOISC 2020 Day2 遗迹 题解 (计数DP)
环球快资讯:MySQL学习笔记-多表查询(上)
当前视讯!量化交易基础 - 011 - 样本外检验
风语筑(603466):上海风语筑文化科技股份有限公司关于股东权益变动比例超过1%的提示性公告
天天观察:希望工程发文感谢《原神》玩家 5天9万多人捐赠240万元
世界聚焦:“刺客”又来了!网友称买到1600元一斤话梅:每颗至少20元
“窄边教科书”上新!戴尔XPS15 9530发布:13代酷睿+RTX 40配8TB SSD
环球微动态丨孟菲斯动物园发大熊猫丫丫新动态 网友:尽快回国!
曝苹果屏下Face ID技术有缺憾:2026年才会趋于完美
C++ STL学习笔记-C++ STL基础
焦点讯息:4-Ribbon负载均衡
信息:可取代eSIM:更完美的iSIM卡来了
二月浏览器大战结果出炉:微软Edge用户数不升反降
头条:《王者荣耀》干将莫邪画中仙皮肤公布:中国古风莫邪绝美
环球微头条丨k8s之list-watch机制、节点调度以及亲和性
全球速讯:记录--虚拟滚动探索与封装