# reels
**Repository Path**: mirrors_andyglick/reels
## Basic Information
- **Project Name**: reels
- **Description**: Actor framework for Java, non-blocking, performant
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-10-31
- **Last Updated**: 2026-02-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# reels
[](https://codecov.io/gh/davidmoten/reels)
[](https://maven-badges.herokuapp.com/maven-central/au.gov.amsa/reels)
Actor framework for Java, non-blocking, performant. Developed as a reaction to the Akka licence change. Akka is a huge framework with a large number of extensions including persistence, web serving, streaming and more. My needs are limited to core of Akka, namely in-memory actor support (including supervision) and that's what this library provides.
Why would you trust this library with plenty of concurrency-sensitive code? I've been involved in concurrency-sensitive projects since 2014 as a frequent contributor to RxJava 1.x and the author of numerous popular RxJava libraries (rxjava-extras, rxjava-jdbc, rxjava3-pool, rxjava-slf4j, rxjava-http, rxjava-aws). All these libraries involve a lot of concurrency-aware and performance-aware work so this is not new ground for me. Please raise an issue if you spot something.
Why do we need another Actor library on the JVM? I think there's space for an in-memory Actor library that has a small footprint (only dependency is *slf4j-api* for logging and jar is ~100K), is easy to use (the API is both simple and discoverable), offers a goodly number of useful features, and is thread-safe and performant.
## Features
* Discoverable concise API (Akka is not, partly due to the way it's evolved and a lot of Scala library stuff)
* Custom supervisors including the ability to retry processing later and/or restart the actor (recreate the Actor object)
* Parent-child actor hierarchies
* Dead letter actor
* SLF4J logging (add the implementation that you like)
* Akka stop semantics (stopping an actor stops its children first)
* Jar size 100K with only one logging dependency (slf4j)
* Built and tested with JDK 8, 11, 17
**Status:** *Published to Maven Central*
TODO
* Lifecycle monitoring (DeathWatch) support. Later.
* Add Actor.preRestart, Actor.postRestart methods? Jury still out on this. NO
* Akka has a kill method that forces onMessage to throw an ActorKilledException. Not keen on this. NO
## How to build
```bash
mvn clean install
```
## Getting started
Add this dependency to your pom.xml:
```xml
com.github.davidmotenreelsVERSION_HERE<
```
## Glossary
For background about the Actor Model see this wikipedia [article](https://en.wikipedia.org/wiki/Actor_model).
**Actor**
An Actor is an object (with a chunk of message processing logic) that reacts to messages that are sent to it and communicates with other Actors via asynchronous messsaging. An Actor can have private state and has the simplification that messages sent to it are processed in-order so concurrency issues don't need to be considered. Concurrent/Parallel aspects of a system are modelled via asynchronous messages between Actors. An Actor has a lifecycle, and can be restarted (recreated) by a Supervisor.
**ActorRef**
An ActorRef is a reference to an Actor and is how one programmatically interacts with an Actor. The referred Actor object is not directly accesible and in fact may be replaced over time if a Supervisor restarts the Actor.
**Context**
A Context (ActorSystem in Akka) is used to create and own Actors in a heirarchy (which can be flat), handle dead messages, and shutdown all its owned Actors in a controlled manner.
**Mailbox**
When a message is sent to an Actor it is placed in a Mailbox (queue) where it waits its turn to be processed by the Actor onMessage method. Mailboxes can be of various types and are customizable. For example a Mailbox can be unbounded in size (so buffered messages growth is only limited by available memory), bounded in size, or supported by a PriorityQueue.
**Message**
The chunk of information that flies around between Actors is a Message and is comprised of content and a sender ActorRef (optional). The reels API also makes available the message recipient ActorRef (self) and Context in the call to Actor.onMessage (Akka exposes those things as functions on `this`).
**Scheduler**
Based on Executors of various kinds Schedulers allow work (Runnable) to be submitted to run now (once a worker thread available) or in the future. Schedulers (and queues) are the basis for asynchronous messaging between Actors.
**Supervisor**
When an Actor throws from its processing methods the error is caught by a Supervisor. Within the Supervisor the ActorRef for the Actor is supplemented with extra methods to be able to restart, restart with a delay or pause an Actor for a period. A minimal Supervisor might just log the failure and allow message processing to continue, or Actor state might be reset via restart, or the Actor might be stopped. Up to you.
**SupervisedActorRef**
When the ActorRef is passed to the Supervisor it is wrapped as a SupervisedActorRef which supplements the ActorRef with methods to restart, restart with a delay or pause the Actor. The aim is that this sort of interaction with an Actor should only happen in the Supervisor (in a fault-recovery scenario).
**Worker**
A Scheduler can create a Worker which has scheduling methods but has a lifespan (controlled by calling the `dispose` method). Every ActorRef has a Worker for scheduling in-order processing of messages. A Scheduler (like the `io()` Scheduler) might offer a unique thread with each Worker so disposal allows for recovery of that thread for use by other Workers.
## Actor Lifecycle UML State Diagram

## Reels UML Class Diagram

## Usage
### Create a Context
* Create a `Context` (`ActorSystem` in Akka) object to create your actors and control their lifecycle
* `Scheduler`s (`Dispatcher`s in Akka) live outside your `Context` object and thus can be shared across `Context`s (for greater efficiency))
The simplest way to create a `Context` object is:
```java
Context context = Context.create();
```
You can use a builder to configure the `Context`:
```java
Context context = Context
.builder()
.supervisor(supervisor)
.deadLetterActorFactory(factory)
.scheduler(scheduler)
.build();
```
### Create an Actor using matchers
Now use a `Context` to create typed Actors:
```java
Context context = Context.create();
ActorRef a = context.matchAny(m -> System.out.println(m.content())).build();
a.tell("hi there");
Thread.sleep(1000);
```
`ActorRef.tell` is asynchronous (with the default scheduler) so we wait with `Thread.sleep` to see something happen. The result of course is that
"hi there" will be written to the console and it will generally happen on a different thread to the call of `tell`.
Here's a "kitchen sink" example that demonstrates many options when creating actors:
```java
Context context = Context.create();
// create a parent actor (you can setup heirarchies) using a builder
ActorRef a = context
.matchAny(m -> {
log.info("{}: parent received {}", m.self(), m.content());
m.self().child("b").tell(m.content(), m.self());
})
.name("a")
.scheduler(Scheduler.single())
.onStop(self -> log.info("{}: onStop", self))
.build();
// create a child actor of `a` using a builder
context
// specify a series of matches to apply to incoming message content
.matchEquals(1, m -> {
log.info("{}: equal matched, sender = {}", m.self(), m.sender());
m.sender().tell(9999));
})
.match(Integer.class, m -> log.info("{}: received integer {}", m.self(), m.content()))
.match(Double.class, m -> log.info("{}: received double {}", m.self(), m.content()))
.matchAny(m -> log.info("{}: received something else {}", m.self(), m.content()))
.name("b")
.onError(e -> log.error(e.getMessage(), e))
.preStart(self -> log.info("{}: preStart", self))
.onStop(self -> log.info("{}: onStop", self))
.scheduler(Scheduler.computation())
.parent(a)
.mailboxFactory(Mailbox.bounded(1000, true))
.supervisor((m, actor, e) -> {
log.error(e.getMessage(), e);
actor.pause(30, TimeUnit.SECONDS);
actor.retry();
})
.build();
a.tell(1);
a.tell(2);
a.tell(3.5);
a.tell(4f);
// give enough time to run (especially for b to respond to `a.tell(1)`
Thread.sleep(500);
context.shutdownGracefully().get(5000, TimeUnit.SECONDS);
```
Output:
```
2022-10-07T21:47:28:055 +1100 [ReelsSingle-1] INFO com.github.davidmoten.reels.ActorTest - a: parent received 1
2022-10-07T21:47:28:057 +1100 [ReelsSingle-1] INFO com.github.davidmoten.reels.ActorTest - a: parent received 2
2022-10-07T21:47:28:057 +1100 [ReelsComputation-1] INFO com.github.davidmoten.reels.ActorTest - b: preStart
2022-10-07T21:47:28:057 +1100 [ReelsComputation-1] INFO com.github.davidmoten.reels.ActorTest - b: equal matched, sender = Optional[a]
2022-10-07T21:47:28:058 +1100 [ReelsComputation-1] INFO com.github.davidmoten.reels.ActorTest - b: received integer 2
2022-10-07T21:47:28:059 +1100 [ReelsSingle-1] INFO com.github.davidmoten.reels.ActorTest - a: parent received 3.5
2022-10-07T21:47:28:059 +1100 [ReelsSingle-1] INFO com.github.davidmoten.reels.ActorTest - a: parent received 4.0
2022-10-07T21:47:28:059 +1100 [ReelsComputation-1] INFO com.github.davidmoten.reels.ActorTest - b: received double 3.5
2022-10-07T21:47:28:060 +1100 [ReelsSingle-1] INFO com.github.davidmoten.reels.ActorTest - a: parent received 9999
2022-10-07T21:47:28:060 +1100 [ReelsComputation-1] INFO com.github.davidmoten.reels.ActorTest - b: received something else 4.0
2022-10-07T21:47:28:060 +1100 [ReelsComputation-1] INFO com.github.davidmoten.reels.ActorTest - b: received integer 9999
2022-10-07T21:47:28:562 +1100 [ReelsComputation-1] INFO com.github.davidmoten.reels.ActorTest - b: onStop
2022-10-07T21:47:28:564 +1100 [ReelsSingle-1] INFO com.github.davidmoten.reels.ActorTest - a: onStop
```
#### A warning about using lambdas to create actors
Note that if you use a lambda and you make a reference to the enclosing class then the ActorRef will retain a reference to the enclosing object. We should be careful about this because that referred object will not be garbage collected till the ActorRef is. Consequently, if you create an actor from another actor then you should be conscious about this lifecycle link.
### Create an Actor using your own class
You can also create an Actor class yourself instead of using the builder. Implement `Actor` or extend `AbstractActor`. Suppose you create a class called `MyActor` which extends `AbstractActor`. You can create an ActorRef for this class with the Context as follows:
```java
ActorRef actor = context.actorClass(MyActor.class).build();
```
or
```java
ActorRef actor = context.actorFactory(() -> new MyActor()).build();
```
Note that you can also use the actorClass builder method with constructor arguments too:
```java
ActorRef actor = context.actorClass(MyActor.class, "Fred Nurk", 1980).build();
```
## Send a message to an Actor
This sends an anonymous message (the Actor wil be supplied ActorRef.none() as the sender) to an actor:
```java
ActorRef actor = ...
Thing thing = ...
actor.tell(thing);
```
To include a sender actor (for example as a reply-to actor):
```java
ActorRef actor = ...
ActorRef