Professional Documents
Culture Documents
Publish
Subscribe in
Akka Cluster
Agenda
• Classic Actors
• Receptionist
• Typed Actors
• Reliable Delivery
2
Motivation
• How do I send a message to an actor without knowing which node it is running on?
• How do I send messages to all actors in the cluster that have registered interest in a named
topic?
3
Classic Actors
4
Introduction
• Mediator actor, akka.cluster.pubsub.DistributedPubSubMediator - registry of actor references and replicates
across cluster.
• It is supposed to be started on all nodes, or all nodes with specified role, in the cluster.
• Can be started with the DistributedPubSub extension or as an ordinary actor.
• Registry eventually consistent. Changes are only performed in the own part of the registry and those
changes are versioned. Deltas are disseminated in a scalable way to other nodes with a gossip protocol.
• Cluster members with status WeaklyUp will participate in Distributed Publish Subscribe, i.e. subscribers
on nodes with WeaklyUp status will receive published messages if the publisher and subscriber are on
same side of a network partition.
• You can send messages via the mediator on any node to registered actors on any other node.
• There a two different modes of message delivery, Publish and Send.
5
6
Publish
• Actors are registered to a named topic. Mediator
• This enables many subscribers on each node.
• The message will be delivered to all
subscribers of the topic. SubscribeAck Subscribe(topic)
ActorSystem
• Message is sent over the wire only once per Subscriber
node(that has a matching topic)
• You register actors to the local mediator Mediator
with DistributedPubSubMediator.Subscribe Mediator
acknowledged with
DistributedPubSubMediator.SubscribeAck and
DistributedPubSubMediator.UnsubscribeAck ActorSystem ActorSystem SubscribeAck
• DistributedPubSubMediator.Publish message Subscribe(topic)
to the local mediator for publishing.
• Actors removed from the registry when
• Terminated Subscriber
• DistributedPubSubMediator.Unsubscribe.
7
Mediator
ActorSystem
Subscriber
gossip
gossip Mediator
Mediator
ActorSystem ActorSystem
Subscriber
gossip
8
Send
• Point-to-point mode, each message is delivered to one destination(unknown).
• The message will be delivered to one recipient with a matching path, if any such exists in
the registry.
• If several entries (registered on several nodes) message sent via the
supplied RoutingLogic (default random) to one destination.
• If sender specifies local affinity - the message is sent to an actor in the same local actor
system as the used mediator actor, if not exists routed to any other matching entry.
• You register actors to the local mediator with DistributedPubSubMediator.Put.
• The ActorRef in Put must belong to the same local actor system as the mediator. The path
without address information is the key to which you send messages.
• Send DistributedPubSubMediator.Send message to the local mediator with the path (without
address information) of the destination actors.
• DistributedPubSubMediator.SendToAll - delivered to all recipients with a matching path.
• allButSelf - message should be sent to a matching path on the self node or not.
• Actors removed from the registry when
• Terminated
• DistributedPubSubMediator.Remove.
Mediator
ActorSystem Put(ActorRef)
user/my-actor
Mediator
Mediator
Sender
Code for Publish
class Subscriber extends Actor with ActorLogging {
import akka.cluster.pubsub.DistributedPubSubMediator.{ Subscribe, SubscribeAck }
val mediator = DistributedPubSub(context.system).mediator
mediator ! Subscribe("content", self) // subscribe to the topic named "content"
def receive = {
case s: String =>
case SubscribeAck(Subscribe("content", None, `self`)) => log.info("subscribing")
} class Publisher extends Actor {
} import akka.cluster.pubsub.DistributedPubSubMediator
// activate the extension
runOn("first") { val mediator = DistributedPubSub(context.system).mediator
context.actorOf(Props[Subscriber](), "subscriber1") def receive = {
} case in: String =>
runOn("second") { val out = in.toUpperCase
context.actorOf(Props[Subscriber](), "subscriber2") mediator ! Publish("content", out)
context.actorOf(Props[Subscriber](), "subscriber3") }
} }
runOn("third") {
val publisher = context.actorOf(Props[Publisher](), "publisher")
later()
// after a while the subscriptions are replicated
publisher ! "hello"
}
Code for Send
class Destination extends Actor with ActorLogging {
import akka.cluster.pubsub.DistributedPubSubMediator.Put
val mediator = DistributedPubSub(context.system).mediator
// register to the path
mediator ! Put(self)
def receive = {
case s: String =>
log.info("Got {}", s)
}
}
14
Receptionist
• You register the specific actors that should be discoverable from each node in the
local Receptionist instance.
• This registry of actor references is then automatically distributed to all other nodes.
• You can lookup such actors with the key that was used when they were registered. The reply to such
a Find request is a Listing, which contains a Set of actor references that are registered for the key. Note that
several actors can be registered to the same key.
• The registry is dynamic. New actors can be registered during the lifecycle of the system.
• Entries are removed –
• registered actors are stopped
• manually deregistered using Receptionist.Deregister
• node they live on is removed from the Cluster.
• Subscribe to changes with the Receptionist.Subscribe message. It will send Listing messages to the subscriber,
first with the set of entries upon subscription, then whenever the entries for a key are changed.
• Subscriptions and Find queries to a clustered receptionist will keep track of cluster reachability and only
list registered actors that are reachable. The full set of actors, including unreachable ones, is available
through Listing.allServiceInstances.
15
Typed Actors
16
The Topic Actor
• Each pub/sub topic represented with an actor, akka.actor.typed.pubsub.Topic.
• Topic actor needs to run on each node where subscribers / publishers live for the topic.
• Identity of the topic - tuple of the type of messages that can be published and a string topic name
• Each topic is represented by one Receptionist service key
• The topic actor acts as a proxy and delegates to the local subscribers handling deduplication.
• When a topic actor has no subscribers for a topic it will deregister itself from the receptionist meaning
published messages for the topic will not be sent to it.
import akka.actor.typed.pubsub.Topic
Behaviors.setup[Command] { context =>
val topic = context.spawn(Topic[Message]("my-topic"),
"MyTopic")
topic ! Topic.Subscribe(subscriberActor)
topic ! Topic.Unsubscribe(subscriberActor)
topic ! Topic.Publish(Message("Hello Subscribers!"))
}
17
Reliable
Delivery
18
Introduction
• Normal message delivery reliability is at-most-once delivery.
• Reliable delivery can’t be achieved automatically under the hood without collaboration from the application.
• There are 3 supported patterns, which are described in the following sections:
• Point-to-point
• Work pulling
• Sharding
19
•For the ProducerController to know where to send the messages, it
must be connected with the ConsumerController. This can be done
21
Sharding
• ShardingConsumerController is identified by
an entityId. A
single ShardingProducerController per
ActorSystem (node) can be shared for sending to
all entities of a certain entity type. No explicit
registration is needed between
the ShardingConsumerController and ShardingPr
oducerController.
• In the
ShardingProducerController.RequestNext messag
e there is information about which entities that
have demand. It is allowed to send to a
new entityId that is not included in
the RequestNext.entitiesWithDemand.
• If sending to an entity that doesn’t have demand
the message will be buffered. Allowed to send
several messages in response to
one RequestNext but it’s recommended to only
send one message and wait for
next RequestNext before sending more messages.
22
Thank You
23