The complete program
main.go
Terminal
Hello, World!. Press Ctrl+C to shut down cleanly.
What just happened
You defined messages
Messages are plain Go values. In standalone mode they are delivered in-process without serialization, so any type works; keep them immutable and side-effect free. Sending across nodes needs registered serializable types, covered in Serialization.You implemented the Actor interface
An actor is any type with three methods:- PreStart: called once before the first message. Initialize dependencies here, not in constructors; returning an error prevents the actor from starting.
- Receive: called for every message, one at a time. Type-switch on
ctx.Message()and callctx.Unhandled()for types you do not handle. - PostStop: called after the mailbox drains during shutdown. Release resources here.
greeted counter needs no mutex. That guarantee
is the heart of the model; Actor Model covers it in depth.
You spawned it and sent messages
Spawn returns a *PID, the handle for all interaction. From outside the actor system you have two patterns:
- Tell: fire-and-forget,
goakt.Tell(ctx, pid, msg). No response, no waiting. - Ask: request-response,
goakt.Ask(ctx, pid, msg, timeout). Blocks until the actor callsctx.Responseexactly once, or the timeout expires.
Receive, the same patterns exist on the context (ctx.Tell, ctx.Ask), plus non-blocking variants; see
Messaging.
ReceiveContext essentials
InsideReceive, ctx provides:
- Message: the message being processed.
- Sender: PID of the sender; nil for scheduled or system messages.
- Self: PID of the current actor.
- Tell / Ask: send to another actor.
- Response: reply to an Ask.
- Unhandled: mark the message as unhandled; it lands in the dead-letter stream.
Where next
- Messaging: Tell, Ask, ordering guarantees, and message design.
- Supervision: what happens when your actor panics.
- Remoting and Clustering: the same API across processes and nodes.
- Testkit: unit-test actors with probes.
- The examples repository: runnable projects for every mode.