
一、背景
1.為什么要做風(fēng)控?
這不得拜產(chǎn)品大佬所賜
目前我們業(yè)務(wù)有使用到非常多的AI能力,如ocr識別、語音測評等,這些能力往往都比較費(fèi)錢或者費(fèi)資源,所以在產(chǎn)品層面也希望我們對用戶的能力使用次數(shù)做一定的限制,因此風(fēng)控是必須的!
2.為什么要自己寫風(fēng)控?
那么多開源的風(fēng)控組件,為什么還要寫呢?是不是想重復(fù)發(fā)明輪子呀.
要想回答這個問題,需要先解釋下我們業(yè)務(wù)需要用到的風(fēng)控(簡稱業(yè)務(wù)風(fēng)控),與開源常見的風(fēng)控(簡稱普通風(fēng)控)有何區(qū)別:

因此,直接使用開源的普通風(fēng)控,一般情況下是無法滿足需求的
3.其它要求
支持實時調(diào)整限制
很多限制值在首次設(shè)置的時候,基本上都是拍定的一個值,后續(xù)需要調(diào)整的可能性是比較大的,因此可調(diào)整并實時生效是必須的
基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
- 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 視頻教程:https://doc.iocoder.cn/video/
二、思路
要實現(xiàn)一個簡單的業(yè)務(wù)風(fēng)控組件,要做什么工作呢?
1.風(fēng)控規(guī)則的實現(xiàn)
a.需要實現(xiàn)的規(guī)則:
- 自然日計數(shù)
- 自然小時計數(shù)
- 自然日+自然小時計數(shù)
自然日+自然小時計數(shù) 這里并不能單純地串聯(lián)兩個判斷,因為如果自然日的判定通過,而自然小時的判定不通過的時候,需要回退,自然日跟自然小時都不能計入本次調(diào)用!
b.計數(shù)方式的選擇:
目前能想到的會有:
- mysql+db事務(wù) 持久化、記錄可溯源、實現(xiàn)起來比較麻煩,稍微“重”了一點(diǎn)
- redis+lua 實現(xiàn)簡單,redis的可執(zhí)行l(wèi)ua腳本的特性也能滿足對“事務(wù)”的要求
- mysql/redis+分布式事務(wù) 需要上鎖,實現(xiàn)復(fù)雜,能做到比較精確的計數(shù),也就是真正等到代碼塊執(zhí)行成功之后,再去操作計數(shù)
目前沒有很精確技術(shù)的要求,代價太大,也沒有持久化的需求,因此選用 redis+lua 即可
2.調(diào)用方式的實現(xiàn)
a.常見的做法 先定義一個通用的入口
//簡化版代碼
@Component
classDetectManager{
funmatchExceptionally(eventId:String,content:String){
//調(diào)用規(guī)則匹配
valrt=ruleService.match(eventId,content)
if(!rt){
throwBaseException(ErrorCode.OPERATION_TOO_FREQUENT)
}
}
}
在service中調(diào)用該方法
//簡化版代碼
@Service
classOcrServiceImpl:OcrService{
@Autowired
privatelateinitvardetectManager:DetectManager
/**
*提交ocr任務(wù)
*需要根據(jù)用戶id來做次數(shù)限制
*/
overridefunsubmitOcrTask(userId:String,imageUrl:String):String{
detectManager.matchExceptionally("ocr",userId)
//doocr
}
}
有沒有更優(yōu)雅一點(diǎn)的方法呢? 用注解可能會更好一點(diǎn)(也比較有爭議其實,這邊先支持實現(xiàn))
由于傳入的 content 是跟業(yè)務(wù)關(guān)聯(lián)的,所以需要通過Spel來將參數(shù)構(gòu)成對應(yīng)的content
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
三、具體實現(xiàn)
1.風(fēng)控計數(shù)規(guī)則實現(xiàn)
a.自然日/自然小時
自然日/自然小時可以共用一套lua
腳本,因為它們只有key
不同,腳本如下:
//lua腳本
localcurrentValue=redis.call('get',KEYS[1]);
ifcurrentValue~=falsethen
iftonumber(currentValue)tonumber(ARGV[1])then
returnredis.call('INCR',KEYS[1]);
else
returntonumber(currentValue)+1;
end;
else
redis.call('set',KEYS[1],1,'px',ARGV[2]);
return1;
end;
其中 KEYS[1]
是日/小時關(guān)聯(lián)的key,ARGV[1]
是上限值,ARGV[2]
是過期時間,返回值則是當(dāng)前計數(shù)值+1后的結(jié)果,(如果已經(jīng)達(dá)到上限,則實際上不會計數(shù))
b.自然日+自然小時 如前文提到的,兩個的結(jié)合實際上并不是單純的拼湊,需要處理回退邏輯
//lua腳本
localdayValue=0;
localhourValue=0;
localdayPass=true;
localhourPass=true;
localdayCurrentValue=redis.call('get',KEYS[1]);
ifdayCurrentValue~=falsethen
iftonumber(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;
localhourCurrentValue=redis.call('get',KEYS[2]);
ifhourCurrentValue~=falsethen
iftonumber(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(notdayPass)andhourPassthen
hourValue=redis.call('DECR',KEYS[2]);
end;
ifdayPassand(nothourPass)then
dayValue=redis.call('DECR',KEYS[1]);
end;
localpair={};
pair[1]=dayValue;
pair[2]=hourValue;
returnpair;
其中 KEYS[1]
是天關(guān)聯(lián)生成的key, KEYS[2]
是小時關(guān)聯(lián)生成的key,ARGV[1]
是天的上限值,ARGV[2]
是小時的上限值,ARGV[3]
是天的過期時間,ARGV[4]
是小時的過期時間,返回值同上
這里給的是比較粗糙的寫法,主要需要表達(dá)的就是,進(jìn)行兩個條件判斷時,有其中一個不滿足,另一個都需要進(jìn)行回退.
2.注解的實現(xiàn)
a.定義一個@Detect注解
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION,AnnotationTarget.CLASS)
annotationclassDetect(
/**
*事件id
*/
valeventId:String="",
/**
*content的表達(dá)式
*/
valcontentSpel:String=""
)
其中content
是需要經(jīng)過表達(dá)式解析出來的,所以接受的是個String
b.定義@Detect注解的處理類
@Aspect
@Component
classDetectHandler{
privatevallogger=LoggerFactory.getLogger(javaClass)
@Autowired
privatelateinitvardetectManager:DetectManager
@Resource(name="detectSpelExpressionParser")
privatelateinitvarspelExpressionParser:SpelExpressionParser
@Bean(name=["detectSpelExpressionParser"])
fundetectSpelExpressionParser():SpelExpressionParser{
returnSpelExpressionParser()
}
@Around(value="@annotation(detect)")
funoperatorAnnotation(joinPoint:ProceedingJoinPoint,detect:Detect):Any?{
if(detect.eventId.isBlank()||detect.contentSpel.isBlank()){
throwillegalArgumentExp("@Detectconfigisnotavailable!")
}
//轉(zhuǎn)換表達(dá)式
valexpression=spelExpressionParser.parseExpression(detect.contentSpel)
valargMap=joinPoint.args.mapIndexed{index,any->
"arg${index+1}"toany
}.toMap()
//構(gòu)建上下文
valcontext=StandardEvaluationContext().apply{
if(argMap.isNotEmpty())this.setVariables(argMap)
}
//拿到結(jié)果
valcontent=expression.getValue(context)
detectManager.matchExceptionally(detect.eventId,content)
returnjoinPoint.proceed()
}
}
需要將參數(shù)放入到上下文中,并起名為arg1
、arg2
....
四、測試一下
1.寫法
使用注解之后的寫法:
//簡化版代碼
@Service
classOcrServiceImpl:OcrService{
@Autowired
privatelateinitvardetectManager:DetectManager
/**
*提交ocr任務(wù)
*需要根據(jù)用戶id來做次數(shù)限制
*/
@Detect(eventId="ocr",contentSpel="#arg1")
overridefunsubmitOcrTask(userId:String,imageUrl:String):String{
//doocr
}
}
2.Debug看看

- 注解值獲取成功
- 表達(dá)式解析成功
審核編輯 :李倩
-
AI
+關(guān)注
關(guān)注
88文章
34781瀏覽量
277162 -
開源
+關(guān)注
關(guān)注
3文章
3647瀏覽量
43676 -
代碼
+關(guān)注
關(guān)注
30文章
4894瀏覽量
70456
原文標(biāo)題:風(fēng)控系統(tǒng)就該這么設(shè)計(萬能通用),穩(wěn)的一批!
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
同星智能入選 “新質(zhì)企業(yè)”第一批種子企業(yè)名單

江西薩瑞微電子榮獲2025年第一批次“數(shù)智工廠”企業(yè)稱號

鴻道Intewell操作系統(tǒng)入選工信部第一批先進(jìn)適用技術(shù)名單

水穩(wěn)拌合站智能監(jiān)測管理系統(tǒng)在水穩(wěn)拌合站質(zhì)量管理中起到關(guān)鍵作用
達(dá)實智能入選深圳市第一批全屋智能建筑應(yīng)用試點(diǎn)項目
手機(jī)和鋰電池納入第一批工業(yè)產(chǎn)品碳足跡核算規(guī)則團(tuán)體標(biāo)準(zhǔn)推薦清單

芯來科技入選上海市新一輪第一批重點(diǎn)“小巨人”企業(yè)
喜報 | 航芯入選新一輪第一批國家重點(diǎn)“小巨人”企業(yè)并將獲中央財政支持

本人研一,做DFB激光穩(wěn)頻,目前只差FPGA ADC DAC做一個反饋系統(tǒng)
總投資超60億元,一批半導(dǎo)體相關(guān)項目簽約江蘇宜興
國電西高成功交貨一批高壓電氣設(shè)備檢測儀器
羅萊迪思入選2024年第一批浙江省制造業(yè)單項冠軍培育企業(yè)!

水穩(wěn)拌合站監(jiān)測管理系統(tǒng)在水穩(wěn)層施工中的價值應(yīng)用

評論