Java规则引擎

依赖


        
            com.googlecode.aviator
            aviator
            3.3.0
        

示例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;

/**
 * 业务需求:
 * "1小时,userid,在ip上,触发action 100次则产生报警"
 *
 * 表达式设计:
 * "redisCount('1','hour',fields('userid,ip,action')) >= 100"
 *
 * 参考资料:https://blog.csdn.net/shihlei/article/details/84919881
 *
 * 事件报警抽象:
 * 需求1:报警判断机制:多长时间内(由时间值和时间单位两个量描述)某个监控对象(允许有多个参数来描述)发生多少次某个事件,则触发一次报警。
 *
 * 需求2:报警沉默机制:多长时间内,最多触发一次报警。
 *
 */
public class RuleEngineDemo {
    public static void main(String[] args) {
        //默认AviatorEvaluator以编译速度优先:
//        AviatorEvaluator.setOptimize(AviatorEvaluator.COMPILE);
        //你可以修改为运行速度优先,这会做更多的编译优化:
        AviatorEvaluator.setOptimize(AviatorEvaluator.EVAL);

        //注册自定义表达式函数
        AviatorEvaluator.addFunction(new FieldsFunction());
        AviatorEvaluator.addFunction(new RedisCountFunction());

        //模拟用户指定规则
//      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);

        //模拟运行时收到数据
        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) {
            // send alarm message
            System.out.printf("发生报警");
        }
    }

    /**
     * 获取字段,校验,生成redis key
     */
    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";
        }
    }

    /**
     * 使用key进行查询,获取redis中存的量且redis +1
     */
    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);//当前支持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进行查询,获取redis中存的量且redis +1
         * 如果key不存在则
         * @param redisKey
         * @return
         */
        private int redisGetAndIncrease(String redisKey, long expireTime) {
            System.out.println("get count from redis : " + redisKey);
            // TODO:检查redisKey是否存在,如果不存在则初始化并为其设置超时时间;如果存在则获取redis中存的量且redis +1

            //这里模拟查询得到的值
            return 300;
        }

        // 获取超时的秒时间
        private long getExpireTime(String period,String timeUnit){
            // 模拟一分钟后超时
            return 60;
        }

        public String getName() {
            return "redisCount";
        }
    }

}

参考资料

  1. Java各种规则引擎
  2. Aviator 表达式求值引擎开源框架
  3. Aviator的规则引擎Demo

你可能感兴趣的:(Java规则引擎)