Detailed guide

Come and use it~

Node development

There are three leaf node types in ice, corresponding to three business abstractions

  • *Flow is used for abstractions that can control business flow, such as various judgments or filter conditions, with clear true and false returns
  • *Result is used for the abstraction of some results, such as issuing various rewards, there are relatively clear true and false returns (for example, in the reward distribution, it should return true if it is issued, and it should return false if it is not issued)
  • *None is used for the abstraction of some unrelated business flows, such as query information, no return value

In the node development process, you can select the abstract leaf node you need to inherit and implement the corresponding method.

  • BaseLeaf* uses IceContext as method parameter, need to implement do* method
  • BaseLeafPack* uses IcePack as method input, need to implement doPack* method
  • BaseLeafRoam* uses IceRoam as method input, need to implement doRoam* method


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

    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

    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.


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.put("e", "a");

Background configuration


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:

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

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

    private Expression compiledExpression;

    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.