Command-Dispatch
Der Command-Dispatch leitet Prozess-Yields an Handler und gibt korrelierte Ergebnisse über die Event-Queues der Prozesse zurück.
Diese Seite ist eine Erweiterungs- und Implementierungsreferenz. Die Ausschnitte für eigene Commands und Dispatcher setzen ein vorhandenes Go-Paket, einen Boot-Graphen, eine Command-API und dienstspezifische Fehlerbehandlung voraus.
Fluss
sequenceDiagram
participant P as Process
participant W as Worker
participant R as Registry
participant H as Handler
P->>W: yield(command, tag)
W->>R: getHandler(cmdID)
R-->>W: handler
W->>H: Handle(cmd, tag, receiver)
H-->>H: async work
H->>W: CompleteYield(tag, result)
W->>P: queue event, wake
P->>P: resume with result
Command-Registry
Die Registry speichert Handler in einer hybriden Struktur:
type Registry struct {
handlers [256]Handler // System commands: O(1) index
extended map[CommandID]Handler // Extended commands: map lookup
frozen atomic.Bool // Lock-free after boot
}
System-Commands (0-255) verwenden Array-Indexierung. Erweiterte Commands verwenden Map-Lookup. Nach Freeze() sind alle Lookups lock-frei.
Command-ID-Bereiche
| Bereich | Modul | Beispiele |
|---|---|---|
| 1-9 | process | Send, Spawn, Terminate, Cancel, Monitor, Unmonitor, Link, Unlink, Exec |
| 10-29 | clock | Sleep, Ticker, Timer |
| 30-39 | socket | Connect, Listen, Accept, Bind, Resolve |
| 50-59 | stream | Read, Write, Close, Seek |
| 60-69 | http | Request, RequestBatch |
| 70-79 | tty | Terminal-E/A |
| 80-89 | websocket | Connect, Send, Receive |
| 90-99 | event | Subscribe, Send |
| 100-119 | sql | Query, Execute, Prepare, Stmt, Tx ops |
| 120-129 | store | Get, Set, Delete, Has |
| 130-139 | security | ValidateToken, CreateToken |
| 140-149 | function | Call, AsyncStart, AsyncCancel |
| 150-159 | exec | ProcessWait |
| 160-169, 173-174 | cloudstorage | Upload, Download, List, Presigned URLs, Multipart, OpenReader |
| 170-171 | eval | Compile, Run |
| 172 | cdc | Subscribe |
| 180-189 | workflow | SideEffect, Exec, Version, UpsertAttrs |
| 190-199 | contract | Open, Call, AsyncCall, AsyncCancel |
| 200-211 | pg (Prozessgruppe) | Join, Leave, GetMembers, GetLocalMembers, WhichGroups, Broadcast, BroadcastLocal, WhichLocalGroups, Monitor, Events, JoinGroups, LeaveGroups |
| 256+ | custom | Benutzerdefinierte Services |
Pakete reservieren die Eigentümerschaft ihrer Command-IDs aus init() heraus mit MustRegisterCommands(). Kollisionen führen bereits bei der Paketinitialisierung zu einer Panic. Während des Ladens der Komponenten bindet jeder Dienst seine Handler über Registrar.Register. Erst nachdem diese Handler installiert sind, wird der Dispatcher eingefroren.
Commands definieren
Commands sind Datenstrukturen mit einer eindeutigen CommandID:
const MyCommand dispatcher.CommandID = 256
type MyCmd struct {
Input string
Option int
}
func (c *MyCmd) CmdID() dispatcher.CommandID { return MyCommand }
Reservieren Sie die Command-ID bei der Paketinitialisierung:
func init() {
dispatcher.MustRegisterCommands("myservice", MyCommand)
}
Dispatcher
Ein Dispatcher gruppiert verwandte Handler. Er implementiert RegisterAll um Handler zu registrieren und Lebenszyklus-Methoden für Setup/Teardown:
type Handler interface {
Handle(ctx context.Context, cmd Command, tag uint64, receiver ResultReceiver) error
}
type ResultReceiver interface {
CompleteYield(tag uint64, data any, err error)
}
type Dispatcher struct {
// service state
}
func (d *Dispatcher) RegisterAll(register func(id dispatcher.CommandID, h dispatcher.Handler)) {
register(myapi.MyCommand, dispatcher.HandlerFunc(d.handleMyCommand))
}
func (d *Dispatcher) handleMyCommand(ctx context.Context, cmd Command, tag uint64, receiver ResultReceiver) error {
c := cmd.(*myapi.MyCmd)
go func() {
result := doWork(c)
if ctx.Err() == nil {
receiver.CompleteYield(tag, result, nil)
}
}()
return nil
}
Als Boot-Komponente registrieren:
func MyDispatcher() boot.Component {
return boot.New(boot.P{
Name: "dispatcher.myservice",
DependsOn: []boot.Name{DispatcherName},
Load: func(ctx context.Context) (context.Context, error) {
reg := dispatcher.GetRegistrar(ctx)
svc := myservice.NewDispatcher()
svc.RegisterAll(reg.Register)
return ctx, nil
},
})
}
Yields und Korrelation
Wenn ein Prozess asynchrone Arbeit benötigt, yieldet er einen Command mit einem Korrelationstag:
type Yield struct {
Cmd Command
Tag uint64 // Process-local counter for correlation
}
Der Worker extrahiert Yields aus StepOutput nach jedem Step und dispatcht sie an Handler. Jedes Tag identifiziert die Anfrage eindeutig, sodass Ergebnisse zurückgemappt werden können.
Siehe auch
- Scheduler – Prozessausführung
- Module – Integration von Lua-Modulen
- Prozessmodell – übergeordnete Konzepte