# ice **Repository Path**: waitmoon/ice ## Basic Information - **Project Name**: ice - **Description**: Java规则引擎-ice(用全新的思想编排规则) 针对复杂/灵活变动业务,提供一个新的抽象编排解决方案,轻量级,高性能并提供可视化操作页面 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://waitmoon.com - **GVP Project**: No ## Statistics - **Stars**: 745 - **Forks**: 219 - **Created**: 2022-06-24 - **Last Updated**: 2026-04-14 ## Categories & Tags **Categories**: rule-engine **Tags**: 规则引擎, 可视化规则引擎, 轻量级规则引擎 ## README

Ice

Ice

A lightweight, zero-dependency rule engine with visual tree-based orchestration

GitHub Stars GitHub Release License Maven Central Go Reference PyPI Docker Pulls

Documentation · Live Demo · Getting Started · Changelog

中文

--- ## What is Ice? Ice is a rule engine that takes a fundamentally different approach: **rules are organized as trees, not chains or tables**. Each node in the tree handles its own logic independently — modifying one node never creates cascading effects on others. Nodes communicate exclusively through a shared, thread-safe data context called **Roam**, rather than referencing each other directly. The result is a system where business rules can be visually configured, hot-reloaded in seconds, and executed in-memory with sub-millisecond latency — all without requiring any database or middleware. ## Key Features | Feature | Description | |---|---| | **Tree-Based Orchestration** | Rules organized as trees — nodes are independent, changes to one node never cascade to others | | **Visual Web Editor** | Configure rules through an intuitive web UI with a tree editor, no DSL to learn | | **Zero Dependencies** | No database, no message queue, no service registry — just files on disk | | **Multi-Language SDKs** | Java, Go, and Python with full feature parity | | **Hot Reload** | Changes take effect in seconds via automatic version polling — no restart needed | | **High Performance** | Pure in-memory execution with sub-millisecond latency, zero network overhead | | **Node Reuse** | Same node can be shared across multiple rule trees | | **Parallel Execution** | Built-in parallel relation nodes for concurrent child execution | | **Mock Debugging** | Trigger rule execution remotely from the Web UI for real-time debugging | | **Lane / Traffic Isolation** | Branch-based rule isolation for A/B testing, canary releases, and gradual rollouts | ## How It Works ### Rule Trees Every rule in Ice is a **tree** composed of two types of nodes: - **Relation Nodes** — control the execution flow (similar to logical operators) - **Leaf Nodes** — contain your actual business logic ### Relation Nodes (Control Flow) | Type | Behavior | Short-Circuit | |------|----------|:---:| | **AND** | All children must return true | Yes | | **ANY** | At least one child returns true | Yes | | **ALL** | All children must return true | No | | **NONE** | No child returns true | No | | **TRUE** | Always returns true (execute all children for side effects) | No | Each type has a **parallel** variant (`P_AND`, `P_ANY`, `P_ALL`, `P_NONE`, `P_TRUE`) that executes children concurrently. ### Leaf Nodes (Business Logic) | Type | Return | Purpose | |------|--------|---------| | **Flow** | `boolean` | Conditional checks — "Is the user eligible?" | | **Result** | `boolean` | Business operations — "Issue the coupon" | | **None** | `void` | Side effects — queries, logging, metrics, notifications | ### Roam (Data Context) **Roam** is the thread-safe data container that flows through the entire rule tree. Nodes read input from and write output to Roam — they never reference each other directly. This is the key to node isolation. ``` Roam ├── put("uid", 12345) // flat key-value ├── putDeep("user.level", "gold") // nested key ├── get("uid") // → 12345 └── getDeep("user.level") // → "gold" ``` - **Thread-safe**: Java `ConcurrentHashMap` / Go `sync.RWMutex` / Python `threading.RLock` - **Deep keys**: `putDeep("a.b.c", value)` auto-creates nested structures - **Dynamic references**: prefix a value with `@` to resolve it from Roam at runtime (e.g., `@uid` reads `roam.get("uid")`) ## Architecture ``` ┌──────────────────────────────────────────────────────────────┐ │ Shared Storage (ice-data/) │ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐ │ │ │ apps/ │ │ bases/ │ │ confs/ │ │ versions/│ │ │ └────────┘ └────────┘ └────────┘ └──────────┘ │ └──────────────────────────────────────────────────────────────┘ ▲ ▲ │ Write │ Read (Poll) │ │ ┌─────────┴─────────┐ ┌───────────┴───────────┐ │ Ice Server │ │ Ice Client │ │ │ │ │ │ • Web UI │ │ • Version polling │ │ • Tree editor │ │ • Hot reload │ │ • Apply & publish │ │ • In-memory exec │ │ • Mock debugging │ │ • Mock execution │ │ │ │ • Fault tolerant │ └────────────────────┘ └───────────────────────┘ ``` **Key design decisions:** - **Server and Client are fully decoupled** — if the Server goes down, Clients continue executing rules from their in-memory cache - **File-based communication** — no network protocol between Server and Client, just a shared directory (`ice-data/`) - **Incremental updates** — Client polls `versions/` for changes and applies only the delta; falls back to full reload if incremental files are missing ### Deployment Options | Mode | Description | |------|-------------| | **Single Machine** | Server + Client on the same host, `ice-data/` is a local directory | | **Docker Compose** | Containerized deployment with volume mounts | | **Distributed** | Multiple Servers/Clients sharing storage via NFS, AWS EFS, or GCP Filestore | ## Quick Start ### 1. Deploy the Server ```bash docker run -d --name ice-server -p 8121:8121 \ -v ./ice-data:/app/ice-data waitmoon/ice-server:latest ``` Open `http://localhost:8121` to access the Web UI. ### 2. Install the Client SDK
Java ```xml com.waitmoon.ice ice-core 4.0.9 ```
Go ```bash go get github.com/zjn-zjn/ice/sdks/go ```
Python ```bash pip install ice-rules ```
### 3. Define Your Leaf Nodes Leaf nodes are where your business logic lives. Here's a simple `ScoreFlow` that checks if a value meets a threshold:
Java ```java @Data @EqualsAndHashCode(callSuper = true) public class ScoreFlow extends BaseLeafFlow { private double score; private String key; @Override protected boolean doFlow(IceRoam roam) { Number value = roam.getDeep(key); if (value == null) { return false; } return value.doubleValue() >= score; } } ```
Go ```go type ScoreFlow struct { Score float64 `json:"score" ice:"name:Score Threshold,desc:Minimum score to pass"` Key string `json:"key" ice:"name:Roam Key,desc:Key to read from Roam"` } func (s *ScoreFlow) DoFlow(ctx context.Context, roam *icecontext.Roam) bool { value := roam.ValueDeep(s.Key).Float64Or(0) return value >= s.Score } ``` Register it: ```go ice.RegisterLeaf("com.example.ScoreFlow", &ice.LeafMeta{Name: "Score Check", Desc: "Check if score meets threshold"}, func() any { return &ScoreFlow{} }) ```
Python ```python @ice.leaf("com.example.ScoreFlow", name="Score Check", desc="Check if score meets threshold") class ScoreFlow: score: Annotated[float, IceField(name="Score Threshold")] = 0.0 key: Annotated[str, IceField(name="Roam Key")] = "" def do_flow(self, roam: Roam) -> bool: value = roam.get_deep(self.key) if value is None: return False return float(value) >= self.score ```
### 4. Initialize the Client and Execute
Java ```java // Initialize IceFileClient client = new IceFileClient(1, "./ice-data", "com.your.package"); client.start(); // Execute IceRoam roam = IceRoam.create(); roam.setId(1L); roam.put("uid", 12345); roam.put("score", 95.5); Ice.syncProcess(roam); ```
Go ```go // Initialize client, _ := ice.NewClient(1, "./ice-data") client.Start() defer client.Destroy() // Execute roam := ice.NewRoam() roam.SetId(1) roam.Put("uid", 12345) roam.Put("score", 95.5) ice.SyncProcess(context.Background(), roam) ```
Python ```python # Initialize client = ice.FileClient(app=1, storage_path="./ice-data") client.start() # Execute roam = ice.Roam.create() roam.set_id(1) roam.put("uid", 12345) roam.put("score", 95.5) ice.sync_process(roam) ```
### 5. Configure Rules in the Web UI Open the Web UI → create a rule tree → arrange your nodes → click **Apply**. Changes take effect within seconds — no restart required. ## Comparison with Traditional Rule Engines | | Ice | Traditional (Drools, etc.) | |---|---|---| | **Learning Curve** | 5 minutes — visual configuration, no DSL | Requires learning rule language syntax | | **Deployment** | Docker one-click, zero external dependencies | Requires database + middleware setup | | **Configuration** | Web UI tree editor | Text/code-based rule files | | **Performance** | In-memory, sub-millisecond latency | Compilation and interpretation overhead | | **Hot Reload** | Seconds, automatic — no restart | Often requires application restart | | **Change Impact** | Node-isolated — changes affect only that node | Cascading effects across rule chains | | **Ops Complexity** | Single binary + file directory | Multi-component infrastructure | ## Use Cases | Scenario | Examples | |----------|---------| | **Marketing Campaigns** | Coupons, discounts, promotions, group deals, flash sales | | **Risk Control** | Credit assessment, anti-fraud detection, real-time risk evaluation | | **Dynamic Pricing** | Price strategies, discount rules, tiered pricing, surge pricing | | **Access Control** | Permission management, feature flags, role-based access | | **Process Orchestration** | Approval workflows, order processing, ticket routing, state machines | ## Configuration Reference ### Client Configuration | Parameter | Required | Default | Description | |-----------|:---:|---------|-------------| | `app` | Yes | — | Application ID | | `storagePath` | Yes | — | Path to shared `ice-data/` directory | | `scan` | Java only | — | Package path for automatic node scanning | | `pollInterval` | No | `2s` | How often to check for version changes | | `heartbeatInterval` | No | `10s` | Heartbeat reporting interval | | `parallelism` | No | ForkJoinPool | Thread pool size for parallel nodes | | `lane` | No | — | Lane name for traffic isolation / branch testing | ### Server Configuration | Parameter | Default | Description | |-----------|---------|-------------| | `port` | `8121` | Server HTTP port | | `storage-path` | `./ice-data` | File storage directory | | `mode` | `open` | `open` (normal) or `controlled` (read-only UI) | | `client-timeout` | `30s` | Client inactivity timeout | | `version-retention` | `1000` | Number of version files to retain | | `publish-targets` | — | Remote server addresses for multi-instance publishing | ## Documentation **Guide** - [Getting Started](https://waitmoon.com/en/guide/getting-started.html) — Deploy, integrate, and run your first rule - [Core Concepts](https://waitmoon.com/en/guide/concepts.html) — Trees, nodes, Roam, and execution model - [Architecture](https://waitmoon.com/en/guide/architecture.html) — Design decisions and deployment patterns - [FAQ](https://waitmoon.com/en/guide/faq.html) — Common questions and troubleshooting **Reference** - [Node Types](https://waitmoon.com/en/reference/node-types.html) — All relation and leaf node types in detail - [Client Configuration](https://waitmoon.com/en/reference/client-config.html) — Full client parameter reference - [Server Configuration](https://waitmoon.com/en/reference/server-config.html) — Full server parameter reference - [Roam API](https://waitmoon.com/en/reference/roam-api.html) — Data context API across all SDKs **SDK Guides** - [Java SDK](https://waitmoon.com/en/sdk/java.html) - [Go SDK](https://waitmoon.com/en/sdk/go.html) - [Python SDK](https://waitmoon.com/en/sdk/python.html) ## Community - [GitHub Issues](https://github.com/zjn-zjn/ice/issues) — Bug reports and feature requests - [GitHub Discussions](https://github.com/zjn-zjn/ice/discussions) — Questions and community discussions - [Live Demo](https://eg.waitmoon.com) — Try Ice without installing anything ## Contributing Contributions are welcome! Whether it's bug reports, feature requests, documentation improvements, or code contributions — feel free to open an issue or submit a pull request. ## License [Apache License 2.0](LICENSE)