项目作者: mikea

项目描述 :
Actor System for Golang
高级语言: Go
项目地址: git://github.com/mikea/tractor.git
创建时间: 2020-11-12T20:16:31Z
项目社区:https://github.com/mikea/tractor

开源协议:GNU Affero General Public License v3.0

下载


build

Tractor

[WORK IN PROGRESS]

Actor System for Golang.

The goal of the project at this stage is to design an idiomatic go api for actor programming model together with common
services provided by an actor system.

Documentation

Defining Actors

Setup Handler

To define an actor the setup handler needs to be provided:

  1. type SetupHandler func(ctx ActorContext) MessageHandler

This callback is executed by the system every time new instance of the actor is created. The job of the callback is to
initialize all resources, send all initial requests, setup and return the message handler responsible for processing
actor mailbox messages.

Message Handler

Message handler callback processes the incoming message and returns a new handler to be used after the message.

  1. type MessageHandler func(message interface{}) MessageHandler

Two special values are recognized:

  • nil - signals to use the same handler that was used to process the message
  • Stopped() - signals to the system to terminate the current actor.

Setup Parameters

To parametrize an actor create a closure binding initialization parameters:

  1. func Countdown(start int) SetupHandler {
  2. return func(ctx ActorContext) MessageHandler {
  3. count := start
  4. return func(msg interface{}) MessageHandler {
  5. count = count - 1
  6. if count == 0 {
  7. return Stopped()
  8. }
  9. return nil
  10. }
  11. }
  12. }

Spawning Actors

Any actor can spawn a child by invoking ctx.Spawn() method with a setup handler:

  1. ctx.Spawn(Countdown(10))

Actor Lifecycle

Signals

Receive messages related to the actor lifecycle by enabling signal delivery in the context:

  1. func(ctx ActorContext) MessageHandler {
  2. ctx.DeliverSignals(true)
  3. return func(msg interface{}) MessageHandler {
  4. switch msg.(type) {
  5. case PostInitSignal:
  6. // first message delivered after the initialization
  7. case PreStopSignal:
  8. // delivered before terminating children
  9. case PostStopSignal:
  10. // delivered after terminating all children
  11. }
  12. return nil
  13. }
  14. }

Watching Actors

Any actor can be notified when any other actor is terminated:

  1. func(ctx ActorContext) MessageHandler {
  2. ctx.Watch(ctx.Spawn(child))
  3. return func(msg interface{}) MessageHandler {
  4. switch msg.(type) {
  5. case Terminated:
  6. // child was terminated
  7. }
  8. return nil
  9. }
  10. }

or with a custom message:

  1. ctx.WatchWith(ctx.Spawn(child), "childTerminated")

Actor Communication

Tell

Sending messages to a different actor is potentially a difficult enterprise and might involve other actor creation. Thus
current actor context is required to send messsages:

  1. child := ctx.Spawn(setupChild)
  2. child.Tell(ctx, "ping")

Sender

During the event processing the sender of the message is available:

  1. return func(msg interface{}) MessageHandler {
  2. if msg == "ping" {
  3. ctx.Sender().Tell(ctx, "pong")
  4. }
  5. return nil
  6. }

Ask

Asking an actor means sending it a message and expecting a reply back.
Ask() method in context provides a bridge between go channels and actor communcation, enabling you to write blocking
code:

  1. reply := <-ctx.Ask(ref, "ping")

Actor System

Starting System

To start the system you neeed to provide setup handler for the root actor:

  1. system := tractor.Start(Root())

Shutting Down

The system shuts down when root actor stops. You can wait for actor system to finish:

  1. system.Wait()

Communicating With System

Communication with the running system is done through the root actor reference and system context:

  1. system.Root().Tell(system.Context(), "request")

Patterns

Typed Reference

Dealing with untyped messages might be challenges to scale. A simple typed wrappers can be created that will define
public api:

  1. type getAndIncrement struct{ }
  2. func Counter() SetupHandler {
  3. return func(ctx ActorContext) MessageHandler {
  4. count := 0
  5. return func(m interface{}) MessageHandler {
  6. switch m.(type) {
  7. case getAndIncrement:
  8. ctx.Sender().Tell(ctx, count)
  9. count++
  10. }
  11. return nil
  12. }
  13. }
  14. }
  15. type CounterRef struct {
  16. Ref ActorRef
  17. }
  18. func (ref CounterRef) GetAndIncrement(ctx ActorContext) chan interface{} {
  19. return ctx.Ask(ref.Ref, getAndIncrement{})
  20. }

Development

Use nix to set up development environment:

  1. nix-shell
  2. go test ./...