Job types in SDK for Go
Connect your Go application to SchedulerX to schedule method execution at regular intervals.
Job type comparison
Choose a job type based on your workload requirements.
| Job type | Execution model | Use case | Min SDK version |
|---|---|---|---|
| Standalone | Single instance runs one method | Periodic tasks on a single node (cleanup, health checks, report generation) | -- |
| Broadcast | All worker nodes run the same method | Fan-out operations across a cluster (cache refresh, config reload) | 0.0.2 |
| Map | Root task distributes subtasks to worker nodes | Parallel processing of a large dataset split into chunks | 0.0.4 |
| MapReduce | Map with an aggregation (Reduce) phase | Parallel processing with result aggregation (distributed counting, order scanning) | 0.0.4 |
Standalone jobs
A standalone job runs on a single instance. Implement the Processor interface to define the job logic.
Interface:
type Processor interface {
Process(ctx *processor.JobContext) (*ProcessResult, error)
}
Example:
package main
import (
"fmt"
"github.com/alibaba/schedulerx-worker-go/processor"
"github.com/alibaba/schedulerx-worker-go/processor/jobcontext"
"time"
)
var _ processor.Processor = &HelloWorld{}
type HelloWorld struct{}
func (h *HelloWorld) Process(ctx *jobcontext.JobContext) (*processor.ProcessResult, error) {
fmt.Println("[Process] Start process my task: Hello world!")
// mock execute task
time.Sleep(3 * time.Second)
ret := new(processor.ProcessResult)
ret.SetStatus(processor.InstanceStatusSucceed)
fmt.Println("[Process] End process my task: Hello world!")
return ret, nil
}
Register and run the job:
package main
import (
"github.com/alibaba/schedulerx-worker-go"
)
func main() {
// Replace with your actual configuration values from the SchedulerX console
cfg := &schedulerx.Config{
Endpoint: "acm.aliyun.com",
Namespace: "433d8b23-xxx-xxx-xxx-90d4d1b9a4af",
GroupId: "xueren_sub",
AppKey: "xxxxxx",
}
client, err := schedulerx.GetClient(cfg)
if err != nil {
panic(err)
}
client.RegisterTask("HelloWorld", &HelloWorld{})
select {}
}
Broadcast jobs
A broadcast job runs on all worker nodes in the group and supports three lifecycle methods.
Broadcast jobs require SDK for Go version 0.0.2 or later.
| Method | Runs on | Purpose |
|---|---|---|
PreProcess |
Master node (once) | Initialize shared state before worker execution |
Process |
All worker nodes | Execute the main job logic; results are collected after all nodes complete |
PostProcess |
Master node (once) | Aggregate results from all worker nodes |
Example:
package main
import (
"fmt"
"github.com/alibaba/schedulerx-worker-go/processor"
"github.com/alibaba/schedulerx-worker-go/processor/jobcontext"
"github.com/alibaba/schedulerx-worker-go/processor/taskstatus"
"math/rand"
"strconv"
)
type TestBroadcast struct{}
// Process all machines would execute it.
func (t TestBroadcast) Process(ctx *jobcontext.JobContext) (*processor.ProcessResult, error) {
value := rand.Intn(10)
fmt.Printf("Total sharding num=%d, sharding=%d, value=%d\n", ctx.ShardingNum(), ctx.ShardingId(), value)
ret := new(processor.ProcessResult)
ret.SetSucceed()
ret.SetResult(strconv.Itoa(value))
return ret, nil
}
// PreProcess only one machine will execute it.
func (t TestBroadcast) PreProcess(ctx *jobcontext.JobContext) error {
fmt.Println("TestBroadcastJob PreProcess")
return nil
}
// PostProcess only one machine will execute it.
func (t TestBroadcast) PostProcess(ctx *jobcontext.JobContext) (*processor.ProcessResult, error) {
fmt.Println("TestBroadcastJob PostProcess")
allTaskResults := ctx.TaskResults()
allTaskStatuses := ctx.TaskStatuses()
num := 0
for key, val := range allTaskResults {
fmt.Printf("%v:%v\n", key, val)
if allTaskStatuses[key] == taskstatus.TaskStatusSucceed {
valInt, _ := strconv.Atoi(val)
num += valInt
}
}
fmt.Printf("TestBroadcastJob PostProcess(), num=%d\n", num)
ret := new(processor.ProcessResult)
ret.SetSucceed()
ret.SetResult(strconv.Itoa(num))
return ret, nil
}
In PostProcess, call ctx.TaskResults() and ctx.TaskStatuses() to access each worker node's output. Check taskstatus.TaskStatusSucceed to filter successful results before aggregation.
Map and MapReduce jobs
Map and MapReduce jobs distribute work across multiple worker nodes. The root task splits work into subtasks, and each subtask runs in parallel on available nodes.
Map and MapReduce jobs require SDK for Go version 0.0.4 or later.
Map jobs
A Map job splits a dataset into subtasks and processes each subtask independently. Implement the MapJobProcessor interface.
-
Implement the job processor. Key points:
-
Embed
*mapjob.MapJobProcessorto inherit Map capabilities. -
Call
mr.IsRootTask(jobCtx)to determine whether the current task is the root task. -
Call
mr.Map(jobCtx, messageList, "Level1Dispatch")to distribute subtasks. The third argument is the task name used to route subtasks in theProcessmethod. -
Read
jobCtx.JobParameters()to accept runtime parameters from the SchedulerX console.
package main import ( "encoding/json" "errors" "fmt" "github.com/alibaba/schedulerx-worker-go/processor" "github.com/alibaba/schedulerx-worker-go/processor/jobcontext" "github.com/alibaba/schedulerx-worker-go/processor/mapjob" "strconv" "time" ) type TestMapJob struct { *mapjob.MapJobProcessor } func (mr *TestMapJob) Kill(jobCtx *jobcontext.JobContext) error { //TODO implement me panic("implement me") } // Process the MapReduce model is used to distributed scan orders for timeout confirmation func (mr *TestMapJob) Process(jobCtx *jobcontext.JobContext) (*processor.ProcessResult, error) { var ( num = 10 err error ) taskName := jobCtx.TaskName() if jobCtx.JobParameters() != "" { num, err = strconv.Atoi(jobCtx.JobParameters()) if err != nil { return nil, err } } if mr.IsRootTask(jobCtx) { fmt.Println("start root task") var messageList []interface{} for i := 1; i <= num; i++ { var str = fmt.Sprintf("id_%d", i) messageList = append(messageList, str) } fmt.Println(messageList) return mr.Map(jobCtx, messageList, "Level1Dispatch") } else if taskName == "Level1Dispatch" { var task []byte = jobCtx.Task() var str string err = json.Unmarshal(task, &str) fmt.Printf("str=%s\n", str) time.Sleep(100 * time.Millisecond) fmt.Println("Finish Process...") if str == "id_5" { return processor.NewProcessResult( processor.WithFailed(), processor.WithResult(str), ), errors.New("test") } return processor.NewProcessResult( processor.WithSucceed(), processor.WithResult(str), ), nil } return processor.NewProcessResult(processor.WithFailed()), nil } -
-
Register the Map job.
package main import ( "github.com/alibaba/schedulerx-worker-go" "github.com/alibaba/schedulerx-worker-go/processor/mapjob" ) func main() { // Replace with your actual configuration values from the SchedulerX console cfg := &schedulerx.Config{ Endpoint: "acm.aliyun.com", Namespace: "433d8b23-xxx-xxx-xxx-90d4d1b9a4af", GroupId: "xueren_sub", AppKey: "xxxxxx", } client, err := schedulerx.GetClient(cfg) if err != nil { panic(err) } task := &TestMapJob{ mapjob.NewMapJobProcessor(), } client.RegisterTask("TestMapJob", task) select {} }
MapReduce jobs
A MapReduce job extends a Map job with a Reduce phase that aggregates results from all subtasks. Implement the MapReduceJobProcessor interface.
-
Implement the job processor. Key points:
-
Embed
*mapjob.MapReduceJobProcessorinstead of*mapjob.MapJobProcessor. -
Implement the
Reducemethod to aggregate results. CalljobCtx.TaskResults()andjobCtx.TaskStatuses()to access subtask outcomes. -
The root task (key
0) is skipped during aggregation because it only dispatches subtasks.
package main import ( "encoding/json" "fmt" "github.com/alibaba/schedulerx-worker-go/processor" "github.com/alibaba/schedulerx-worker-go/processor/jobcontext" "github.com/alibaba/schedulerx-worker-go/processor/mapjob" "github.com/alibaba/schedulerx-worker-go/processor/taskstatus" "strconv" "time" ) type OrderInfo struct { Id string `json:"id"` Value int `json:"value"` } func NewOrderInfo(id string, value int) *OrderInfo { return &OrderInfo{Id: id, Value: value} } type TestMapReduceJob struct { *mapjob.MapReduceJobProcessor } func (mr *TestMapReduceJob) Kill(jobCtx *jobcontext.JobContext) error { //TODO implement me panic("implement me") } // Process the MapReduce model is used to distributed scan orders for timeout confirmation func (mr *TestMapReduceJob) Process(jobCtx *jobcontext.JobContext) (*processor.ProcessResult, error) { var ( num = 1000 err error ) taskName := jobCtx.TaskName() if jobCtx.JobParameters() != "" { num, err = strconv.Atoi(jobCtx.JobParameters()) if err != nil { return nil, err } } if mr.IsRootTask(jobCtx) { fmt.Println("start root task, taskId=%d", jobCtx.TaskId()) var orderInfos []interface{} for i := 1; i <= num; i++ { orderInfos = append(orderInfos, NewOrderInfo(fmt.Sprintf("id_%d", i), i)) } return mr.Map(jobCtx, orderInfos, "OrderInfo") } else if taskName == "OrderInfo" { orderInfo := new(OrderInfo) if err := json.Unmarshal(jobCtx.Task(), orderInfo); err != nil { fmt.Printf("task is not OrderInfo, task=%+v\n", jobCtx.Task()) } fmt.Printf("taskId=%d, orderInfo=%+v\n", jobCtx.TaskId(), orderInfo) time.Sleep(1 * time.Millisecond) return processor.NewProcessResult( processor.WithSucceed(), processor.WithResult(strconv.Itoa(orderInfo.Value)), ), nil } return processor.NewProcessResult(processor.WithFailed()), nil } func (mr *TestMapReduceJob) Reduce(jobCtx *jobcontext.JobContext) (*processor.ProcessResult, error) { allTaskResults := jobCtx.TaskResults() allTaskStatuses := jobCtx.TaskStatuses() count := 0 fmt.Printf("reduce: all task count=%d\n", len(allTaskResults)) for key, val := range allTaskResults { if key == 0 { continue } if allTaskStatuses[key] == taskstatus.TaskStatusSucceed { num, err := strconv.Atoi(val) if err != nil { return nil, err } count += num } } fmt.Printf("reduce: succeed task count=%d\n", count) return processor.NewProcessResult( processor.WithSucceed(), processor.WithResult(strconv.Itoa(count)), ), nil } -
-
Register the MapReduce job.
package main import ( "github.com/alibaba/schedulerx-worker-go" "github.com/alibaba/schedulerx-worker-go/processor/mapjob" ) func main() { // Replace with your actual configuration values from the SchedulerX console cfg := &schedulerx.Config{ Endpoint: "acm.aliyun.com", Namespace: "433d8b23-xxx-xxx-xxx-90d4d1b9a4af", GroupId: "xueren_sub", AppKey: "xxxxxx", } client, err := schedulerx.GetClient(cfg) if err != nil { panic(err) } task := &TestMapReduceJob{ mapjob.NewMapReduceJobProcessor(), } client.RegisterTask("TestMapReduceJob", task) select {} }
Stop a running job
Go processors run jobs in goroutines, which cannot be forcibly terminated. Implement the KillProcessor interface to signal Process to stop gracefully.
Stopping jobs requires SDK for Go version 1.0.2 or later.
Use a flag variable that Kill sets and Process checks in its loop:
package main
import (
"fmt"
"time"
"github.com/alibaba/schedulerx-worker-go/processor"
"github.com/alibaba/schedulerx-worker-go/processor/jobcontext"
)
var _ processor.Processor = &HelloWorld{}
type HelloWorld struct{}
var Stop = false
func (h *HelloWorld) Process(ctx *jobcontext.JobContext) (*processor.ProcessResult, error) {
fmt.Println("[Process] Start process my task: Hello world!")
// mock execute task
for i := 0; i < 10; i++ {
fmt.Printf("Hello%d\n", i)
time.Sleep(2 * time.Second)
if Stop {
break
}
}
ret := new(processor.ProcessResult)
ret.SetSucceed()
fmt.Println("[Process] End process my task: Hello world!")
return ret, nil
}
func (h *HelloWorld) Kill(ctx *jobcontext.JobContext) error {
fmt.Println("[Kill] Start kill my task: Hello world!")
Stop = true
return nil
}
When SchedulerX sends a stop signal, the SDK invokes Kill. The Process method detects the flag change on its next loop iteration and exits cleanly.