Actor Registration and Messaging
This guide explains how to register actors, spawn them, and send messages using Spring Boot Starter Actor.
Registering Actors
In Spring Boot Starter Actor, actors are registered as Spring components. This allows them to be automatically discovered and managed by the Spring container.
Creating a Simple Actor
To create an actor, implement the SpringActor
interface and annotate the class with @Component
:
import io.github.seonwkim.core.SpringActor;
import io.github.seonwkim.core.SpringActorContext;
import org.apache.pekko.actor.typed.ActorRef;
import org.apache.pekko.actor.typed.Behavior;
import org.apache.pekko.actor.typed.javadsl.ActorContext;
import org.apache.pekko.actor.typed.javadsl.Behaviors;
import org.springframework.stereotype.Component;
@Component
public class HelloActor implements SpringActor<HelloActor, HelloActor.Command> {
// Define the command interface for messages this actor can handle
public interface Command {}
// Define a message type
public static class SayHi implements Command {}
// Define a message type
public static class SayHello implements Command {
public final ActorRef<String> replyTo;
public SayHello(ActorRef<String> replyTo) {
this.replyTo = replyTo;
}
}
// Create the behavior for this actor
@Override
public Behavior<Command> create(SpringActorContext actorContext) {
return Behaviors.setup(ctx -> new HelloActorBehavior(ctx, actorContext).create());
}
// Inner class to handle the actor's behavior
private static class HelloActorBehavior {
private final ActorContext<Command> ctx;
private final SpringActorContext actorContext;
HelloActorBehavior(ActorContext<Command> ctx, SpringActorContext actorContext) {
this.ctx = ctx;
this.actorContext = actorContext;
}
public Behavior<Command> create() {
return Behaviors.receive(Command.class)
.onMessage(SayHi.class, this::onSayHi)
.onMessage(SayHello.class, this::onSayHello)
.build();
}
private Behavior<Command> onSayHi(SayHi msg) {
ctx.getLog().info("Received SayHi for id={}", actorId);
return Behaviors.same();
}
private Behavior<Command> onSayHello(SayHello msg) {
ctx.getLog().info("Received SayHello for id={}", actorContext.getId());
msg.replyTo.tell("Hello from actor " + actorId);
return Behaviors.same();
}
}
}
Spawning Actors
Once you've registered your actor, you can spawn instances of it using the SpringActorSystem
:
Simplified API
The recommended way to spawn actors is using the fluent builder API:
import io.github.seonwkim.core.SpringActorRef;
import io.github.seonwkim.core.SpringActorSystem;
import org.springframework.stereotype.Service;
import java.time.Duration;
import reactor.core.publisher.Mono;
@Service
public class HelloService {
private final SpringActorRef<HelloActor.Command> helloActor;
public HelloService(SpringActorSystem springActorSystem) {
// Spawn an actor using the fluent builder API
this.helloActor = springActorSystem
.spawn(HelloActor.class)
.withId("default")
.withTimeout(Duration.ofSeconds(3))
.startAndWait();
}
// Service methods...
}
Async Spawning
For non-blocking actor creation:
@Service
public class HelloService {
private final CompletionStage<SpringActorRef<HelloActor.Command>> helloActor;
public HelloService(SpringActorSystem springActorSystem) {
// Spawn asynchronously
this.helloActor = springActorSystem
.spawn(HelloActor.class)
.withId("default")
.withTimeout("3s") // Can use string format
.start();
}
public Mono<String> hello() {
return Mono.fromCompletionStage(
helloActor.thenCompose(actor ->
actor.ask(HelloActor.SayHello::new, Duration.ofSeconds(3))
)
);
}
}
Advanced Configuration
The fluent API supports additional configuration options:
SpringActorRef<HelloActor.Command> actor = springActorSystem
.spawn(HelloActor.class)
.withId("myActor")
.withTimeout(Duration.ofSeconds(5))
.withMailbox("bounded") // or "unbounded", "default"
.asClusterSingleton() // For cluster singleton actors
.withContext(customContext) // Custom actor context
.start();
Sending Messages to Actors
Once you have a reference to an actor, you can send messages to it:
Tell Pattern (Fire-and-Forget)
The tell pattern is used when you don't need a response from the actor:
Ask Pattern (Request-Response)
The ask pattern is used when you expect a response from the actor:
public Mono<String> hello() {
return Mono.fromCompletionStage(
helloActor.ask(HelloActor.SayHello::new, Duration.ofSeconds(3)));
}
Stopping Actors
You can gracefully stop actors when they are no longer needed:
Simplified Stop API
Best Practices
- Actor Hierarchy: Organize actors in a hierarchy to manage their lifecycle and supervision.
- Message Immutability: Ensure that messages sent to actors are immutable to prevent concurrency issues.
- Timeout Handling: Always specify reasonable timeouts for ask operations and handle timeout exceptions.
- Non-Blocking Operations: Avoid blocking operations inside actors, as they can lead to thread starvation.
- Actor Naming: Use meaningful and unique names for actors to make debugging easier.
- Prefer Fluent API: Use the fluent builder API for spawning actors as it provides better readability and type safety.
Next Steps
Now that you know how to register actors, spawn them, and send messages, you can:
- Learn how to create sharded actors for clustered environments
- Explore the API Reference for detailed information about the library's APIs