Ice Rule Engine Detailed Guide

Comprehensive guide to mastering visual business orchestration with Ice rule engine

Rule Engine Node Development

Ice rule engine uses node-based design where each node represents an independent business logic unit. By combining different node types, you can implement complex business rule orchestration.

Three Leaf Node Types

Ice rule engine provides three leaf node types for different business scenarios:

1. Flow Node - Process Control

  • Purpose: Control business flow in the rule engine
  • Scenarios: Condition judgment, filtering rules, permission validation
  • Return Value: Clear true (pass) or false (fail)
  • Examples: User level judgment, amount range validation, time condition filtering

2. Result Node - Result Processing

  • Purpose: Execute specific business operations in the rule engine
  • Scenarios: Reward distribution, inventory deduction, send notifications
  • Return Value: true (execution success) or false (execution failure)
  • Examples: Coupon distribution, points reward, balance recharge

3. None Node - Auxiliary Operations

  • Purpose: Auxiliary nodes that don't affect business flow
  • Scenarios: Data query, logging, information assembly
  • Return Value: No return value (none)
  • Examples: User info query, event tracking, cache warming

Node Base Class Selection

Ice rule engine provides three base classes for developers to inherit, choose based on parameter type:

  • BaseLeaf* - Uses IceContext as method parameter, implement do* method
  • BaseLeafPack* - Uses IcePack as method parameter, implement doPack* method
  • BaseLeafRoam* - Uses IceRoam as method parameter, implement doRoam* method

eg:

/**
 * Issue balance node
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class AmountResult extends BaseLeafRoamResult { //The uid that needs to be issued is obtained from roam, so you can inherit BaseLeafRoamResult

    @Resource
    private SendService sendService; //If it is a spring application, you can use springbean directly. For non-Spring applications, please initialize the IceBeanFactory of IceBeanUtils to assemble the instances you need

    private String key; //Configurable uidKey

    private double value; //The configurable balance value value

    @Override
    protected boolean doRoamResult(IceRoam roam) { //You need to implement doRoamResult (that is, your own business content)
        Integer uid = roam.getMulti(key); //Get the uid of the user who needs to issue the balance from roam
        if (uid == null || value <= 0) {
            return false;
        }
        boolean res = sendService.sendAmount(uid, value); //Call the third-party interface to issue the balance (send the balance of value to uid)
        roam.put("SEND_AMOUNT", res); //In the business, I want to put the release result back into roam, maybe for subsequent use
        return res; //return the release result
    }
}

Execute Ice

Assemble IcePack

pack is the package that needs to be assembled before executing ice

  • iceId is the ID of the rule to be triggered, corresponding to the ID of the configuration background, iceId can only trigger one configured rule
  • scene The scene that needs to be triggered, all the rules subscribed to the scene will be triggered
  • confId trigger rule with any node ID as root
  • requestTime request time, default System.currentTimeMillis()
  • roam put the parameters and other information required to execute the rule
  • traceId link ID, automatically generated by default
  • debug log printing, refer to DebugEnum The final debug executed is handler.debug|pack.debug

Call the Ice method

  • void syncProcess(IcePack pack) is executed synchronously
  • List<Future<IceContext>> asyncProcess(IcePack pack) executes asynchronously and returns futures

In the business, information such as execution results may be added to the roam, and the desired data can be obtained from the roam after the execution is completed.

IceRoam

Roam provides the data source required for node execution or stores execution results for subsequent execution. Roam is an extension of ConcurrentHashMap.

  • put/get rewrites the put/get of ConcurrentHashMap, ignoring the key/value null pointer exception of ConcurrentHashMap
  • putValue/getValue ignores the type matching check, saving the forced conversion operation, pay attention to whether the type matches when using
  • putMulti/getMulti use "." to separate and build a hierarchical data structure
  • getUnion If the parameter is a string starting with "@", it will go to the roam to get the data and return it, otherwise it will return the parameter itself
roam.putValue("a", 1); //{"a":1}
roam.getValue("a"); //1
roam.putMulti("b.c", 2); //{"a":1,"b":{"c":2}}
roam.putMulti("b.d", 3); //{"a":1,"b":{"c":2,"d":3}}
roam.getMutli("b"); //{"c":2,"d":3}
roam.getMutli("b.c"); //2
roam.getUnion("a"); //"a"
roam.getUnion("@a"); //1
roam.getUnion(1); //1
roam.put("e", "@a");
roam.getUnion("@e");//1
roam.put("e", "a");
roam.getUnion("@e");//"a"

Background configuration

app

app is used to distinguish different applications, such as ice-test with app=1, when ice-test starts, it will go to ice-server to pull all the configurations of app 1 and initialize it according to the configuration

Add ice

  • ID iceId can be triggered by iceId
  • name description
  • Scenario Subscription scenarios, you can use "," to separate subscriptions to multiple scenarios, which will be triggered when any scenario occurs
  • Configuration ID The root node ID of the ice tree
  • Debug log printing, refer to DebugEnum, and accumulate the values ​​corresponding to the content to be printed, such as printing IN_PACK (Pack-1 before executing ice) and PROCESS (Executing process-2)
  • operate
    • Edit Edit ice
    • View Details View detailed node configuration
    • Backup Backup configuration
    • Backup History can be restored from historical backups
    • Export Export the current configuration (including unpublished changes)

configure node

Click a node to pop up related operations

  • View/Edit Nodes
  • Add child nodes only relationship nodes
  • Add pre-node Add pre-execution node
  • Convert Node can convert the current node to any node
  • Move Node Up and Down Move Node
  • Delete this node The deletion of a node is a soft delete, it is just disconnected, not physically deleted, it can be added back by adding a node ID

Other configuration:

  • confName is the class name of the leaf node. When adding a leaf node for the first time, you need to manually enter the full class name, and there will be a check to see if the class actually exists in the client. When adding a leaf node, a running client for verification
  • Node ID Adding child nodes by node ID is the embodiment of object-level reusability. Nodes with the same ID will only have one copy in memory, and if you change one of them, the rest will change together.
  • record node's debug is only used to display in processInfo
  • inverse inverts the node, if the node should return false, it will return true after inversion, so node classes such as ContainsFlow do not need to develop an additional NotContainsFlow

publish, clear, import, export

  • Release All changes will only be real hot updated to the client after they are released, and unreleased change nodes will have a "^" mark
  • clear clears all changes, reverts to last release
  • import import configuration
  • export export the current configuration (including unpublished changes)
  • Instance selection
    • Server server configuration, currently only supports editing operations in Server mode
    • Client:host/app/uniqueId corresponds to the real configuration display in the client, only supports viewing operations

Combining with expression engine

ice can integrate various expression engines such as Aviator to simplify node configuration.

eg: Taking Aviator as an example, if you want to make an Aviator-based Flow node:

@Data
@EqualsAndHashCode(callSuper = true)
public class AviatorFlow extends BaseLeafRoamFlow {

    private String exp;//Aviator expression for configuration and hot update

    private Expression compiledExpression;

    @Override
    protected boolean doRoamFlow(IceRoam roam) {
        return (boolean) compiledExpression.execute(roam);
    }

    public void setExp(String exp) { //For better performance, recompile when setting/updating exp
        this.exp = exp;
        this.compiledExpression = AviatorEvaluator.compile(exp, true);
    }
}

Node error handling

  • Uniform processing: IceErrorHandle

Set the handle instance of IceErrorHandle (inherit IceErrorHandle and implement the handle method), IceErrorHandle.setHandle(IceErrorHandle customHandle) to change the unified error handling of all nodes; the default implementation is DefaultIceErrorHandle: directly return NodeRunStateEnum.SHUT_DOWN, do not process and terminate the entire process.

  • The leaf node overrides the errorHandle method

The leaf node rewrites the NodeRunStateEnum errorHandle(IceContext cxt, Throwable t) method to handle the error that occurs in the current leaf node. If NodeRunStateEnum.SHUT_DOWN is returned, the entire process will be terminated. If the leaf node overrides the errorHandle method. There will be no unified error handling, but you can go through super.errorHandle(cxt, t) for unified processing again.

  • Configuration processing

The node provides the iceErrorStateEnum configuration, if the configuration is not empty, it has the highest priority and will use the configuration as the return value first. The configuration processing only affects the return value, and the errorHandle method/unified handle processing method rewritten by the leaf node will still be executed.