依賴
<!--規(guī)則引擎2:Aviator 表達(dá)式求值引擎開源框架-->
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>3.3.0</version>
</dependency>
示例Demo
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorLong;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorString;
import java.util.HashMap;
import java.util.Map;
/**
* 業(yè)務(wù)需求:
* "1小時(shí)葵硕,userid校坑,在ip上馆衔,觸發(fā)action 100次則產(chǎn)生報(bào)警"
*
* 表達(dá)式設(shè)計(jì):
* "redisCount('1','hour',fields('userid,ip,action')) >= 100"
*
* 參考資料:https://blog.csdn.net/shihlei/article/details/84919881
*
* 事件報(bào)警抽象:
* 需求1:報(bào)警判斷機(jī)制:多長時(shí)間內(nèi)(由時(shí)間值和時(shí)間單位兩個(gè)量描述)某個(gè)監(jiān)控對(duì)象(允許有多個(gè)參數(shù)來描述)發(fā)生多少次某個(gè)事件澎埠,則觸發(fā)一次報(bào)警躏将。
*
* 需求2:報(bào)警沉默機(jī)制:多長時(shí)間內(nèi),最多觸發(fā)一次報(bào)警歇终。
*
*/
public class RuleEngineDemo {
public static void main(String[] args) {
//默認(rèn)AviatorEvaluator以編譯速度優(yōu)先:
// AviatorEvaluator.setOptimize(AviatorEvaluator.COMPILE);
//你可以修改為運(yùn)行速度優(yōu)先卫病,這會(huì)做更多的編譯優(yōu)化:
AviatorEvaluator.setOptimize(AviatorEvaluator.EVAL);
//注冊(cè)自定義表達(dá)式函數(shù)
AviatorEvaluator.addFunction(new FieldsFunction());
AviatorEvaluator.addFunction(new RedisCountFunction());
//模擬用戶指定規(guī)則
// String expression = "(redisCount('1','hour',fields('userid,ip,action')) >= 100)&&(redisCount('1','hour',fields('userid,ip,action')) <= 200)";
String expression = "redisCount('1','hour',fields('userid,ip,action')) <= 100";
Expression compiledExp = AviatorEvaluator.compile(expression);
//模擬運(yùn)行時(shí)收到數(shù)據(jù)
Map<String, Object> fields = new HashMap<>();
fields.put("userid", "9527");
fields.put("ip", "127.0.0.1");
fields.put("phone", "18811223344");
fields.put("action", "click");
// 報(bào)警規(guī)則判斷
Boolean needAlarm = (Boolean) compiledExp.execute(fields);
if (needAlarm) {
// send alarm message
System.out.printf("發(fā)生報(bào)警");
}
}
/**
* 獲取字段,校驗(yàn)亿胸,生成redis key
*/
static class FieldsFunction extends AbstractFunction {
@Override
public AviatorObject call(Map<String, Object> env, AviatorObject fieldsStrObj) {
//獲取可變參數(shù)
String fieldStr = fieldsStrObj.stringValue(env);
String[] fields = fieldStr.split(",");
StringBuilder redisKey = new StringBuilder();
System.out.println("FieldsFunction : " + fieldStr);
for (String f : fields) {
Object value = env.get(f);
if (value != null) {
redisKey.append(value.toString());
} else {
//TODO 參數(shù)合法性校驗(yàn)
}
redisKey.append(":");
}
//TODO key如果過長的話坯钦,會(huì)影響redis的性能
return new AviatorString(redisKey.toString());
}
public String getName() {
return "fields";
}
}
/**
* 使用key進(jìn)行查詢,獲取redis中存的量且redis +1
*/
static class RedisCountFunction extends AbstractFunction {
@Override
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
String period = FunctionUtils.getStringValue(arg1, env);
String timeUnit = FunctionUtils.getStringValue(arg2, env);//當(dāng)前支持hour和minutes
String redisKey = FunctionUtils.getStringValue(arg3, env);
System.out.println("FieldsFunction : " + period + " , " + timeUnit + " , " + redisKey);
long expire = getExpireTime(period,timeUnit);
//TODO 讀取redis
int redisCount = redisGetAndIncrease(redisKey, expire);
return new AviatorLong(redisCount);
}
/**
* 使用key進(jìn)行查詢损敷,獲取redis中存的量且redis +1
* 如果key不存在則
* @param redisKey
* @return
*/
private int redisGetAndIncrease(String redisKey, long expireTime) {
System.out.println("get count from redis : " + redisKey);
// TODO:檢查redisKey是否存在葫笼,如果不存在則初始化并為其設(shè)置超時(shí)時(shí)間深啤;如果存在則獲取redis中存的量且redis +1
//這里模擬查詢得到的值
return 300;
}
// 獲取超時(shí)的秒時(shí)間
private long getExpireTime(String period,String timeUnit){
// 模擬一分鐘后超時(shí)
return 60;
}
public String getName() {
return "redisCount";
}
}
}
參考資料
- Java各種規(guī)則引擎
- Aviator 表達(dá)式求值引擎開源框架
- Aviator的規(guī)則引擎Demo