编写不易,转载请注明(http://shihlei.iteye.com/blog/2421576)!
一 概述
Aviator 是一个Java 语言实现的表达式引擎,可以接受字符串类型的表达式,带入参数求值。
需求场景:
用户日志字段:[ip,phone,userid,action]
希望灵活组合这些字段,生成规则,比如 “1小时,userid,在ip上,触发action 100次报警”,并能灵活修改或新增。
这种需求基于Avaiator实现非常方便,实现规则引擎后,规则修改组合就不需要研发干预了。
本文基于Aviator自定义函数实现一个规则引擎的简单demo,关于读redis计数,报警等非重点仅提供空实现,重在描述思路。
依赖:
com.googlecode.aviator aviator 3.3.0
二 简单Demo
(1)带入参数求和:
package x.expression.aviator;
import java.util.HashMap;
import java.util.Map;
import com.googlecode.aviator.AviatorEvaluator;
public class AviatorDemo {
public static void main(String[] args) {
String expression = "a + b + c";
Map params = new HashMap<>();
params.put("a", 1);
params.put("b", 2);
params.put("c", 3);
long result = (long) AviatorEvaluator.execute(expression, params);
System.out.printf("result : " + result);
}
}
(2)自定义函数:
Aviator 自定义函数,需要实现 com.googlecode.aviator.runtime.type.AviatorFunction , 覆盖想要的方法,然后注册即可使用;
package x.expression.aviator;
import java.util.HashMap;
import java.util.Map;
import com.googlecode.aviator.AviatorEvaluator;
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;
public class AviatorSelfFunctionDemo {
public static void main(String[] args) {
//注册函数
AviatorEvaluator.addFunction(new MySumFunction());
String expression = "my_sum(a,b,c)";
Map params = new HashMap<>();
params.put("a", 1);
params.put("b", 2);
params.put("c", 3);
long result = (long) AviatorEvaluator.execute(expression, params);
System.out.printf("result : " + result);
}
/**
* 自定义函数,实现三元数据求和
*/
static class MySumFunction extends AbstractFunction {
@Override
public AviatorObject call(Map env, AviatorObject a, AviatorObject b, AviatorObject c) {
Number numA = FunctionUtils.getNumberValue(a, env);
Number numB = FunctionUtils.getNumberValue(b, env);
Number numC = FunctionUtils.getNumberValue(c, env);
long result = numA.longValue() + numB.longValue() + numC.longValue();
return new AviatorLong(result);
}
/**
* 获取函数名
*
* @return 函数名
*/
public String getName() {
return "my_sum";
}
}
}
三 规则引擎
(1)设计
业务需求:
"1小时,userid,在ip上,触发action 100次报警"
表达式设计:
"redisCount('1','hour',fields('userid,ip,action')) >= 100"
函数说明:
fields() : 获取字段,校验,生成redis key
redisCount():使用 key进行查询,获取redis中存的量且redis +1
(2)实现
package x.expression.aviator;
import java.util.HashMap;
import java.util.Map;
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;
public class RuleEngineDemo {
public static void main(String[] args) {
//注册自定义表达式函数
AviatorEvaluator.addFunction(new FieldsFunction());
AviatorEvaluator.addFunction(new RedisCountFunction());
//用户指定规则
String expression = "redisCount('1','hour',fields('userid,ip,action')) >= 100";
Expression compiledExp = AviatorEvaluator.compile(expression);
//运行时收到数据
Map fields = new HashMap();
fields.put("userid", "9527");
fields.put("ip", "127.0.0.1");
fields.put("phone", "18811223344");
fields.put("action", "click");
Boolean needAlarm = (Boolean) compiledExp.execute(fields);
if (needAlarm) {
System.out.printf("报警");
}
}
static class FieldsFunction extends AbstractFunction {
@Override
public AviatorObject call(Map env, AviatorObject fieldsStrObj) {
//获取可变参数
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 参数合法性校验
}
redisKey.append(":");
}
//TODO key 长多过长,会影响redis性能
return new AviatorString(redisKey.toString());
}
public String getName() {
return "fields";
}
}
static class RedisCountFunction extends AbstractFunction {
@Override
public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
String period = FunctionUtils.getStringValue(arg1, env);
String timeUnit = FunctionUtils.getStringValue(arg2, env);
String redisKey = FunctionUtils.getStringValue(arg3, env);
System.out.println("FieldsFunction : " + period + " , " + timeUnit + " , " + redisKey);
//TODO 读取redis
int redisCount = redisGetAndIncrease(redisKey);
return new AviatorLong(redisCount);
}
private int redisGetAndIncrease(String redisKey) {
System.out.println("get redis : " + redisKey);
//这里查询redis获得活动的值;
return 10000;
}
public String getName() {
return "redisCount";
}
}
}