Go SDK
Ice Go SDK, with full feature parity with the Java SDK. Native support for context.Context, integrating trace and timeout control.
Installation
Requires Go 1.21+.
go get github.com/zjn-zjn/ice/sdks/goQuick Start
package main
import (
"context"
"fmt"
"log"
ice "github.com/zjn-zjn/ice/sdks/go"
icecontext "github.com/zjn-zjn/ice/sdks/go/context"
)
// Define a leaf node
type ScoreFlow struct {
Score float64 `json:"score" ice:"name:Score Threshold,desc:Threshold for score evaluation"`
Key string `json:"key" ice:"name:Value Key,desc:Key to retrieve value from roam"`
}
func (s *ScoreFlow) DoRoamFlow(ctx context.Context, roam *icecontext.Roam) bool {
value := roam.GetFloat64(s.Key, 0)
return value >= s.Score
}
func init() {
ice.RegisterLeaf("com.example.ScoreFlow",
&ice.LeafMeta{Name: "Score Check", Desc: "Check if score meets threshold"},
func() any { return &ScoreFlow{} })
}
func main() {
client, err := ice.NewClient(1, "./ice-data")
if err != nil {
log.Fatal(err)
}
client.Start()
defer client.Destroy()
pack := ice.NewPack().SetIceId(1)
pack.Roam.Put("score", 85.0)
ctxList := ice.SyncProcess(context.Background(), pack)
for _, iceCtx := range ctxList {
fmt.Println("Result:", iceCtx.Pack.Roam.Data())
}
}Leaf Node Development
Registration
The Go SDK uses explicit registration (not annotation scanning):
ice.RegisterLeaf(className string, meta *LeafMeta, factory func() any)className: Node class name, corresponding to confName in Server configurationmeta: Node metadata (name, description, aliases), can be nilfactory: Factory function to create node instances
9 Node Interfaces
All interfaces take context.Context as their first parameter:
| Type | Interface Method | Return Value | Description |
|---|---|---|---|
| Flow | DoRoamFlow(ctx, roam) | bool | Conditional check (most common) |
DoPackFlow(ctx, pack) | bool | Needs Pack access | |
DoFlow(ctx, iceCtx) | bool | Needs full Context | |
| Result | DoRoamResult(ctx, roam) | bool | Business operation (most common) |
DoPackResult(ctx, pack) | bool | Needs Pack access | |
DoResult(ctx, iceCtx) | bool | Needs full Context | |
| None | DoRoamNone(ctx, roam) | — | Auxiliary operation (most common) |
DoPackNone(ctx, pack) | — | Needs Pack access | |
DoNone(ctx, iceCtx) | — | Needs full Context |
The SDK automatically detects which interface is implemented during registration; no manual type specification needed.
Field Configuration
Use the ice struct tag to add field descriptions:
type MyNode struct {
Score float64 `json:"score" ice:"name:Score Threshold,desc:Threshold for score evaluation"`
Key string `json:"key" ice:"name:Value Key"`
// Hidden from configuration interface
Service *http.Client `json:"-"` // json:"-" excludes from serialization
Cache map[string]any `json:"cache" ice:"-"` // ice:"-" hides from config
}Aliases (Cross-Language Compatibility)
ice.RegisterLeaf("com.example.ScoreFlow",
&ice.LeafMeta{
Alias: []string{"score_flow", "ScoreFlow"},
},
func() any { return &ScoreFlow{} })Aliases allow Go nodes to respond to class names configured by Java/Python SDKs.
Executing Rules
All execution methods take context.Context as their first parameter:
ctx := context.Background()
// Execute by iceId
pack := ice.NewPack().SetIceId(1)
pack.Roam.Put("uid", 12345)
ctxList := ice.SyncProcess(ctx, pack)
// Execute by scene
pack := ice.NewPack().SetScene("recharge")
ctxList := ice.SyncProcess(ctx, pack)
// Async execution
channels := ice.AsyncProcess(ctx, pack)
for _, ch := range channels {
iceCtx := <-ch
// Process result
}Convenience Methods
roam := ice.ProcessSingleRoam(ctx, pack)
roams := ice.ProcessRoam(ctx, pack)
iceCtx := ice.ProcessSingleCtx(ctx, pack)
ctxList := ice.ProcessCtx(ctx, pack)Context Propagation
func handleRequest(w http.ResponseWriter, r *http.Request) {
ctx := ice.WithTraceId(r.Context(), r.Header.Get("X-Trace-Id"))
pack := ice.NewPack().SetScene("api")
pack.Roam.Put("userId", getUserId(r))
// ctx is propagated to all leaf nodes; logs automatically include traceId
ice.SyncProcess(ctx, pack)
}Roam Operations
roam := ice.NewRoam()
// Basic operations
roam.Put("name", "Alice")
name := roam.GetString("name")
age := roam.GetInt("age", 0) // With default value
score := roam.GetFloat64("score", 0.0)
// Multi-level key
roam.PutMulti("user.profile.level", 5)
level := roam.GetMulti("user.profile.level")
// Reference syntax
roam.GetUnion("@user.profile.level") // 5See Roam API Reference for the complete API.
Client Configuration
// Minimal setup
client, _ := ice.NewClient(1, "./ice-data")
// Full configuration
client, _ := ice.NewClientWithOptions(
1, // app ID
"./ice-data", // storage path
-1, // parallelism (-1 uses default)
5*time.Second, // poll interval
30*time.Second, // heartbeat interval
"", // lane (empty string = main trunk)
)
// With lane
client, _ := ice.NewWithLane(1, "./ice-data", "feature-xxx")Lifecycle
client.Start()
client.WaitStarted()
version := client.GetLoadedVersion()
client.Destroy()Logging Configuration
Uses slog by default. Custom loggers are supported:
type MyLogger struct{}
func (l *MyLogger) Debug(ctx context.Context, msg string, args ...any) { /* ... */ }
func (l *MyLogger) Info(ctx context.Context, msg string, args ...any) { /* ... */ }
func (l *MyLogger) Warn(ctx context.Context, msg string, args ...any) { /* ... */ }
func (l *MyLogger) Error(ctx context.Context, msg string, args ...any) { /* ... */ }
ice.SetLogger(&MyLogger{})TraceId is automatically extracted from context and appended to logs.
Next Steps
- Java SDK | Python SDK -- Other language SDKs
- Node Type Reference -- All relation and leaf node types
- Client Configuration Reference -- Complete configuration options