GitHub - vladopajic/go-actor: A lightweight library for writing concurrent programs in Go using the Actor model. (original) (raw)

go-actor

test lint coverage Go Report Card GoDoc Release

goactor-cover

go-actor is a lightweight library for writing concurrent programs in Go using the Actor model.

Motivation

Without reusable design principles, maintaining a complex codebase can be challenging, as developers implement logic differently when no common practice is defined.

go-actor aims to provide a pattern for building highly efficient programs, giving developers a straightforward approach to designing components, the building blocks of programs, using a pattern based on the Actor Model and Communicating Sequential Processes (CSP).

Advantage

Abstractions

The core abstraction layer of go-actor consists of three primary interfaces:

Examples

Explore the examples repository to see go-actor in action. Reviewing these examples is highly recommended, as they will greatly enhance your understanding of the library.

// This example will demonstrate how to create actors for producer-consumer use case. // Producer will create incremented number on every 1 second interval and // consumer will print whatever number it receives. func main() { mbx := actor.NewMailboxint

// Produce and consume workers are created with same mailbox
// so that produce worker can send messages directly to consume worker
p := actor.New(&producerWorker{mailbox: mbx})
c1 := actor.New(&consumerWorker{mailbox: mbx, id: 1})

// Note: Example creates two consumers for the sake of demonstration
// since having one or more consumers will produce the same result. 
// Message on stdout will be written by first consumer that reads from mailbox.
c2 := actor.New(&consumerWorker{mailbox: mbx, id: 2})

// Combine all actors to singe actor so we can start and stop all at once
a := actor.Combine(mbx, p, c1, c2).Build()
a.Start()
defer a.Stop()

// Stdout output:
// consumed 1      (worker 1)
// consumed 2      (worker 2)
// consumed 3      (worker 1)
// consumed 4      (worker 2)
// ...

select {}

}

// producerWorker will produce incremented number on 1 second interval type producerWorker struct { mailbox actor.MailboxSender[int] num int }

func (w *producerWorker) DoWork(ctx actor.Context) actor.WorkerStatus { select { case <-ctx.Done(): return actor.WorkerEnd

case <-time.After(time.Second):
    w.num++
    w.mailbox.Send(ctx, w.num)

    return actor.WorkerContinue
}

}

// consumerWorker will consume numbers received on mailbox type consumerWorker struct { mailbox actor.MailboxReceiver[int] id int }

func (w *consumerWorker) DoWork(ctx actor.Context) actor.WorkerStatus { select { case <-ctx.Done(): return actor.WorkerEnd

case num := <-w.mailbox.ReceiveC():
    fmt.Printf("consumed %d \t(worker %d)\n", num, w.id)

    return actor.WorkerContinue
}

}

Add-ons

While go-actor is designed to be a minimal library with lean interfaces, developers can extend its functionality with domain-specific add-ons. Some notable add-ons include:

Pro Tips

To enhance code quality in projects that heavily rely on the actor model with go-actor, consider adhering to best practices and reviewing common hurdles for frequently encountered issues.

Design Decisions

You can find detailed design decisions here.

Benchmarks

See library benchmarks here.

Contribution

All contributions are useful, whether it is a simple typo, a more complex change, or just pointing out an issue. We welcome any contribution so feel free to open PR or issue.

Continue reading here.

Happy coding 🌞