2017-05-10 16:20:38 +02:00
|
|
|
# Cluster Client
|
2013-04-14 22:30:09 +02:00
|
|
|
|
|
|
|
|
An actor system that is not part of the cluster can communicate with actors
|
2017-05-10 16:20:38 +02:00
|
|
|
somewhere in the cluster via this `ClusterClient`. The client can of course be part of
|
2013-04-14 22:30:09 +02:00
|
|
|
another cluster. It only needs to know the location of one (or more) nodes to use as initial
|
2017-05-10 16:20:38 +02:00
|
|
|
contact points. It will establish a connection to a `ClusterReceptionist` somewhere in
|
2013-04-14 22:30:09 +02:00
|
|
|
the cluster. It will monitor the connection to the receptionist and establish a new
|
|
|
|
|
connection if the link goes down. When looking for a new receptionist it uses fresh
|
|
|
|
|
contact points retrieved from previous establishment, or periodically refreshed contacts,
|
2017-03-31 13:52:05 +03:00
|
|
|
i.e. not necessarily the initial contact points.
|
2015-06-30 16:22:33 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
@@@ note
|
|
|
|
|
|
|
|
|
|
`ClusterClient` should not be used when sending messages to actors that run
|
|
|
|
|
within the same cluster. Similar functionality as the `ClusterClient` is
|
|
|
|
|
provided in a more efficient way by @ref:[Distributed Publish Subscribe in Cluster](distributed-pub-sub.md) for actors that
|
|
|
|
|
belong to the same cluster.
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
@@@
|
2015-04-27 14:25:10 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
Also, note it's necessary to change `akka.actor.provider` from `local`
|
|
|
|
|
to `remote` or `cluster` when using
|
2017-03-31 13:52:05 +03:00
|
|
|
the cluster client.
|
2015-04-27 14:25:10 +02:00
|
|
|
|
2013-04-14 22:30:09 +02:00
|
|
|
The receptionist is supposed to be started on all nodes, or all nodes with specified role,
|
2017-05-10 16:20:38 +02:00
|
|
|
in the cluster. The receptionist can be started with the `ClusterClientReceptionist` extension
|
2013-04-14 22:30:09 +02:00
|
|
|
or as an ordinary actor.
|
|
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
You can send messages via the `ClusterClient` to any actor in the cluster that is registered
|
|
|
|
|
in the `DistributedPubSubMediator` used by the `ClusterReceptionist`.
|
|
|
|
|
The `ClusterClientReceptionist` provides methods for registration of actors that
|
|
|
|
|
should be reachable from the client. Messages are wrapped in `ClusterClient.Send`,
|
|
|
|
|
`ClusterClient.SendToAll` or `ClusterClient.Publish`.
|
2013-04-22 13:03:29 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
Both the `ClusterClient` and the `ClusterClientReceptionist` emit events that can be subscribed to.
|
|
|
|
|
The `ClusterClient` sends out notifications in relation to having received a list of contact points
|
|
|
|
|
from the `ClusterClientReceptionist`. One use of this list might be for the client to record its
|
2016-05-04 04:50:16 -07:00
|
|
|
contact points. A client that is restarted could then use this information to supersede any previously
|
|
|
|
|
configured contact points.
|
|
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
The `ClusterClientReceptionist` sends out notifications in relation to having received a contact
|
2017-05-10 16:20:38 +02:00
|
|
|
from a `ClusterClient`. This notification enables the server containing the receptionist to become aware of
|
2016-05-04 04:50:16 -07:00
|
|
|
what clients are connected.
|
|
|
|
|
|
2017-05-18 18:39:23 +12:00
|
|
|
1. **ClusterClient.Send**
|
2013-04-22 13:03:29 +02:00
|
|
|
|
2017-05-18 18:39:23 +12:00
|
|
|
The message will be delivered to one recipient with a matching path, if any such
|
|
|
|
|
exists. If several entries match the path the message will be delivered
|
2017-06-05 17:33:56 +09:00
|
|
|
to one random destination. The sender of the message can specify that local
|
2017-05-18 18:39:23 +12:00
|
|
|
affinity is preferred, i.e. the message is sent to an actor in the same local actor
|
|
|
|
|
system as the used receptionist actor, if any such exists, otherwise random to any other
|
|
|
|
|
matching entry.
|
2013-04-22 13:03:29 +02:00
|
|
|
|
2017-05-18 18:39:23 +12:00
|
|
|
2. **ClusterClient.SendToAll**
|
2013-04-22 13:03:29 +02:00
|
|
|
|
2017-05-18 18:39:23 +12:00
|
|
|
The message will be delivered to all recipients with a matching path.
|
2013-04-22 13:03:29 +02:00
|
|
|
|
2017-05-18 18:39:23 +12:00
|
|
|
3. **ClusterClient.Publish**
|
2013-04-22 13:03:29 +02:00
|
|
|
|
2017-05-18 18:39:23 +12:00
|
|
|
The message will be delivered to all recipients Actors that have been registered as subscribers
|
|
|
|
|
to the named topic.
|
2013-04-14 22:30:09 +02:00
|
|
|
|
|
|
|
|
Response messages from the destination actor are tunneled via the receptionist
|
2017-06-05 17:33:56 +09:00
|
|
|
to avoid inbound connections from other cluster nodes to the client:
|
|
|
|
|
|
|
|
|
|
* @scala[`sender()`,] @java[`getSender()`,] as seen by the destination actor, is not the client itself,
|
|
|
|
|
but the receptionist
|
|
|
|
|
* @scala[`sender()`] @java[`getSender()`] of the response messages, sent back from the destination and seen by the client,
|
|
|
|
|
is `deadLetters`
|
|
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
since the client should normally send subsequent messages via the `ClusterClient`.
|
2016-03-18 17:06:34 +01:00
|
|
|
It is possible to pass the original sender inside the reply messages if
|
2015-08-18 17:27:42 +02:00
|
|
|
the client is supposed to communicate directly to the actor in the cluster.
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
While establishing a connection to a receptionist the `ClusterClient` will buffer
|
2013-09-09 14:56:16 +02:00
|
|
|
messages and send them when the connection is established. If the buffer is full
|
2017-05-10 16:20:38 +02:00
|
|
|
the `ClusterClient` will drop old messages when new messages are sent via the client.
|
2015-06-10 19:58:45 +02:00
|
|
|
The size of the buffer is configurable and it can be disabled by using a buffer size of 0.
|
2013-09-09 14:56:16 +02:00
|
|
|
|
2015-06-10 19:58:45 +02:00
|
|
|
It's worth noting that messages can always be lost because of the distributed nature
|
|
|
|
|
of these actors. As always, additional logic should be implemented in the destination
|
|
|
|
|
(acknowledgement) and in the client (retry) actors to ensure at-least-once message delivery.
|
2017-03-31 13:52:05 +03:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
## An Example
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-03-31 13:52:05 +03:00
|
|
|
On the cluster nodes first start the receptionist. Note, it is recommended to load the extension
|
2017-05-10 16:20:38 +02:00
|
|
|
when the actor system is started by defining it in the `akka.extensions` configuration property:
|
2013-08-20 01:43:34 -07:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
```
|
|
|
|
|
akka.extensions = ["akka.cluster.client.ClusterClientReceptionist"]
|
|
|
|
|
```
|
2013-08-20 01:43:34 -07:00
|
|
|
|
|
|
|
|
Next, register the actors that should be available for the client.
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
Scala
|
|
|
|
|
: @@snip [ClusterClientSpec.scala]($akka$/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala) { #server }
|
|
|
|
|
|
|
|
|
|
Java
|
|
|
|
|
: @@snip [ClusterClientTest.java]($akka$/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java) { #server }
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
On the client you create the `ClusterClient` actor and use it as a gateway for sending
|
2013-04-14 22:30:09 +02:00
|
|
|
messages to the actors identified by their path (without address information) somewhere
|
|
|
|
|
in the cluster.
|
|
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
Scala
|
|
|
|
|
: @@snip [ClusterClientSpec.scala]($akka$/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala) { #client }
|
|
|
|
|
|
|
|
|
|
Java
|
|
|
|
|
: @@snip [ClusterClientTest.java]($akka$/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java) { #client }
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
The `initialContacts` parameter is a @scala[`Set[ActorPath]`]@java[`Set<ActorPath>`], which can be created like this:
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
Scala
|
|
|
|
|
: @@snip [ClusterClientSpec.scala]($akka$/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala) { #initialContacts }
|
|
|
|
|
|
|
|
|
|
Java
|
|
|
|
|
: @@snip [ClusterClientTest.java]($akka$/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java) { #initialContacts }
|
2013-04-14 22:30:09 +02:00
|
|
|
|
|
|
|
|
You will probably define the address information of the initial contact points in configuration or system property.
|
2017-05-11 17:27:57 +02:00
|
|
|
See also [Configuration](#cluster-client-config).
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
A more comprehensive sample is available in the tutorial named
|
|
|
|
|
@scala[[Distributed workers with Akka and Scala](https://github.com/typesafehub/activator-akka-distributed-workers).]
|
|
|
|
|
@java[[Distributed workers with Akka and Java](https://github.com/typesafehub/activator-akka-distributed-workers-java).]
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
## ClusterClientReceptionist Extension
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
In the example above the receptionist is started and accessed with the `akka.cluster.client.ClusterClientReceptionist` extension.
|
2013-04-14 22:30:09 +02:00
|
|
|
That is convenient and perfectly fine in most cases, but it can be good to know that it is possible to
|
2017-05-10 16:20:38 +02:00
|
|
|
start the `akka.cluster.client.ClusterReceptionist` actor as an ordinary actor and you can have several
|
2013-04-14 22:30:09 +02:00
|
|
|
different receptionists at the same time, serving different types of clients.
|
|
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
Note that the `ClusterClientReceptionist` uses the `DistributedPubSub` extension, which is described
|
|
|
|
|
in @ref:[Distributed Publish Subscribe in Cluster](distributed-pub-sub.md).
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2013-08-20 01:43:34 -07:00
|
|
|
It is recommended to load the extension when the actor system is started by defining it in the
|
2017-05-10 16:20:38 +02:00
|
|
|
`akka.extensions` configuration property:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
akka.extensions = ["akka.cluster.client.ClusterClientReceptionist"]
|
|
|
|
|
```
|
2013-04-14 22:30:09 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
## Events
|
2015-04-27 14:25:10 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
As mentioned earlier, both the `ClusterClient` and `ClusterClientReceptionist` emit events that can be subscribed to.
|
2016-05-04 04:50:16 -07:00
|
|
|
The following code snippet declares an actor that will receive notifications on contact points (addresses to the available
|
2017-05-10 16:20:38 +02:00
|
|
|
receptionists), as they become available. The code illustrates subscribing to the events and receiving the `ClusterClient`
|
2016-05-04 04:50:16 -07:00
|
|
|
initial state.
|
|
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
Scala
|
|
|
|
|
: @@snip [ClusterClientSpec.scala]($akka$/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala) { #clientEventsListener }
|
|
|
|
|
|
|
|
|
|
Java
|
|
|
|
|
: @@snip [ClusterClientTest.java]($akka$/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java) { #clientEventsListener }
|
2016-05-04 04:50:16 -07:00
|
|
|
|
2018-01-09 17:41:33 +08:00
|
|
|
Similarly we can have an actor that behaves in a similar fashion for learning what cluster clients are connected to a `ClusterClientReceptionist`:
|
2016-05-04 04:50:16 -07:00
|
|
|
|
2017-06-05 17:33:56 +09:00
|
|
|
Scala
|
|
|
|
|
: @@snip [ClusterClientSpec.scala]($akka$/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala) { #receptionistEventsListener }
|
|
|
|
|
|
|
|
|
|
Java
|
|
|
|
|
: @@snip [ClusterClientTest.java]($akka$/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java) { #receptionistEventsListener }
|
2016-05-04 04:50:16 -07:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
## Dependencies
|
2015-04-27 14:25:10 +02:00
|
|
|
|
|
|
|
|
To use the Cluster Client you must add the following dependency in your project.
|
|
|
|
|
|
2017-05-12 16:07:51 +03:00
|
|
|
sbt
|
|
|
|
|
: @@@vars
|
|
|
|
|
```
|
2017-06-04 16:16:33 +02:00
|
|
|
"com.typesafe.akka" %% "akka-cluster-tools" % "$akka.version$"
|
2017-05-12 16:07:51 +03:00
|
|
|
```
|
|
|
|
|
@@@
|
|
|
|
|
|
|
|
|
|
Maven
|
|
|
|
|
: @@@vars
|
|
|
|
|
```
|
|
|
|
|
<dependency>
|
|
|
|
|
<groupId>com.typesafe.akka</groupId>
|
|
|
|
|
<artifactId>akka-cluster-tools_$scala.binary_version$</artifactId>
|
|
|
|
|
<version>$akka.version$</version>
|
|
|
|
|
</dependency>
|
|
|
|
|
```
|
|
|
|
|
@@@
|
2015-06-30 16:22:33 +02:00
|
|
|
|
2017-05-11 17:27:57 +02:00
|
|
|
<a id="cluster-client-config"></a>
|
2017-05-10 16:20:38 +02:00
|
|
|
## Configuration
|
2017-03-31 13:52:05 +03:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
The `ClusterClientReceptionist` extension (or `ClusterReceptionistSettings`) can be configured
|
2015-06-30 16:22:33 +02:00
|
|
|
with the following properties:
|
|
|
|
|
|
2017-05-11 11:59:28 +03:00
|
|
|
@@snip [reference.conf]($akka$/akka-cluster-tools/src/main/resources/reference.conf) { #receptionist-ext-config }
|
2015-06-30 16:22:33 +02:00
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
The following configuration properties are read by the `ClusterClientSettings`
|
|
|
|
|
when created with a `ActorSystem` parameter. It is also possible to amend the `ClusterClientSettings`
|
|
|
|
|
or create it from another config section with the same layout as below. `ClusterClientSettings` is
|
|
|
|
|
a parameter to the `ClusterClient.props` factory method, i.e. each client can be configured
|
2015-06-30 16:22:33 +02:00
|
|
|
with different settings if needed.
|
2017-03-31 13:52:05 +03:00
|
|
|
|
2017-05-11 11:59:28 +03:00
|
|
|
@@snip [reference.conf]($akka$/akka-cluster-tools/src/main/resources/reference.conf) { #cluster-client-config }
|
2017-05-10 16:20:38 +02:00
|
|
|
|
|
|
|
|
## Failure handling
|
2016-01-07 19:54:55 +01:00
|
|
|
|
|
|
|
|
When the cluster client is started it must be provided with a list of initial contacts which are cluster
|
|
|
|
|
nodes where receptionists are running. It will then repeatedly (with an interval configurable
|
2017-05-10 16:20:38 +02:00
|
|
|
by `establishing-get-contacts-interval`) try to contact those until it gets in contact with one of them.
|
2016-01-07 19:54:55 +01:00
|
|
|
While running, the list of contacts are continuously updated with data from the receptionists (again, with an
|
2017-05-10 16:20:38 +02:00
|
|
|
interval configurable with `refresh-contacts-interval`), so that if there are more receptionists in the cluster
|
2016-01-07 19:54:55 +01:00
|
|
|
than the initial contacts provided to the client the client will learn about them.
|
|
|
|
|
|
|
|
|
|
While the client is running it will detect failures in its connection to the receptionist by heartbeats
|
|
|
|
|
if more than a configurable amount of heartbeats are missed the client will try to reconnect to its known
|
|
|
|
|
set of contacts to find a receptionist it can access.
|
|
|
|
|
|
2017-05-10 16:20:38 +02:00
|
|
|
## When the cluster cannot be reached at all
|
|
|
|
|
|
2016-01-07 19:54:55 +01:00
|
|
|
It is possible to make the cluster client stop entirely if it cannot find a receptionist it can talk to
|
2017-05-10 16:20:38 +02:00
|
|
|
within a configurable interval. This is configured with the `reconnect-timeout`, which defaults to `off`.
|
2016-01-07 19:54:55 +01:00
|
|
|
This can be useful when initial contacts are provided from some kind of service registry, cluster node addresses
|
|
|
|
|
are entirely dynamic and the entire cluster might shut down or crash, be restarted on new addresses. Since the
|
2017-05-10 16:20:38 +02:00
|
|
|
client will be stopped in that case a monitoring actor can watch it and upon `Terminate` a new set of initial
|
2017-05-18 18:39:23 +12:00
|
|
|
contacts can be fetched and a new cluster client started.
|