Aviator-based rule engine Demo

 

It is not easy to write, please indicate when reprinting (http://shihlei.iteye.com/blog/2421576)!

I. Overview

Aviator is an expression engine implemented in the Java language, which can accept string-type expressions and bring in parameters for evaluation.

 

Demand scenario:

User log fields: [ip, phone, userid, action]

 

I hope to flexibly combine these fields to generate rules, such as "1 hour, userid, on ip, trigger action 100 alarms", and flexibly modify or add new ones.

 

This requirement is very convenient to implement based on Avaiator. After the rule engine is implemented, the rule modification combination does not require R&D intervention.

 

This article implements a simple demo of a rule engine based on the Aviator custom function. For non-key points such as reading redis counts and alarms, only empty implementations are provided, and the focus is on describing ideas.

 

rely:

        <dependency>
            <groupId>com.googlecode.aviator</groupId>
            <artifactId>aviator</artifactId>
            <version>3.3.0</version>
        </dependency>

  

Two simple Demo

(1) Bring in the parameter summation:

 

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<String, Object> 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) Custom function:

Aviator custom function, you need to implement com.googlecode.aviator.runtime.type.AviatorFunction, override the desired method, and then register to use;

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) {
        //register function
        AviatorEvaluator.addFunction(new MySumFunction());

        String expression = "my_sum(a,b,c)";

        Map<String, Object> 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);
    }


    /**
     * Custom function to achieve ternary data summation
     */
    static class MySumFunction extends AbstractFunction {

        @Override
        public AviatorObject call(Map<String, Object> 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);
        }

        /**
         * Get the function name
         *
         * @return function name
         */
        public String getName() {
            return "my_sum";
        }
    }
}

 

Three rules engine

(1) Design

Business needs:

"1 hour, userid, on ip, trigger action 100 alarms"

Expression Design:

"redisCount('1','hour',fields('userid,ip,action')) >= 100"

Function description:

fields() : get fields, verify, generate redis key

redisCount(): Use key to query, get the amount stored in redis and redis +1 

 

(2) Realization

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) {
        //Register custom expression function
        AviatorEvaluator.addFunction(new FieldsFunction());
        AviatorEvaluator.addFunction(new RedisCountFunction());


        //User specified rules
        String expression = "redisCount('1','hour',fields('userid,ip,action')) >= 100";
        Expression compiledExp = AviatorEvaluator.compile(expression);


        // receive data at runtime
        Map<String, Object> fields = new HashMap<String, Object>();
        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("Alarm");
        }
    }


    static class FieldsFunction extends AbstractFunction {

        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject fieldsStrObj) {
            // get variable parameters
            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 parameter validity check
                }
                redisKey.append(":");
            }

            //TODO key is too long, it will affect redis performance
            return new AviatorString(redisKey.toString());
        }

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


    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);
            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);
            / / Here query redis to get the value of the activity;
            return 10000;
        }

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

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325335011&siteId=291194637