18
18
19
19
import akka .actor .AbstractActor ;
20
20
import akka .actor .ActorRef ;
21
+ import akka .actor .ActorSystem ;
22
+ import akka .actor .Address ;
21
23
import akka .actor .Cancellable ;
22
24
import akka .actor .Props ;
23
25
import akka .actor .Scheduler ;
24
26
import akka .cluster .Cluster ;
25
27
import akka .cluster .ClusterEvent ;
28
+ import akka .cluster .sharding .ClusterSharding ;
29
+ import akka .cluster .sharding .ShardRegion ;
26
30
import com .arpnetworking .clusteraggregator .models .ShardAllocation ;
27
31
import com .arpnetworking .metrics .Metrics ;
28
32
import com .arpnetworking .metrics .MetricsFactory ;
33
+ import com .arpnetworking .steno .Logger ;
34
+ import com .arpnetworking .steno .LoggerFactory ;
29
35
import com .arpnetworking .utility .ParallelLeastShardAllocationStrategy ;
30
36
import com .google .common .collect .ArrayListMultimap ;
37
+ import com .google .common .collect .Maps ;
31
38
import com .google .common .collect .Multimaps ;
32
39
import com .google .common .collect .Sets ;
33
40
import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
34
41
import scala .compat .java8 .OptionConverters ;
35
42
import scala .concurrent .duration .Duration ;
43
+ import scala .concurrent .duration .FiniteDuration ;
36
44
37
45
import java .io .Serializable ;
38
46
import java .util .Collection ;
@@ -55,22 +63,33 @@ public class ClusterStatusCache extends AbstractActor {
55
63
/**
56
64
* Creates a {@link akka.actor.Props} for use in Akka.
57
65
*
58
- * @param cluster The cluster to reference.
66
+ *
67
+ * @param system The Akka {@link ActorSystem}.
68
+ * @param pollInterval The {@link java.time.Duration} for polling state.
59
69
* @param metricsFactory A {@link MetricsFactory} to use for metrics creation.
60
70
* @return A new {@link akka.actor.Props}
61
71
*/
62
- public static Props props (final Cluster cluster , final MetricsFactory metricsFactory ) {
63
- return Props .create (ClusterStatusCache .class , cluster , metricsFactory );
72
+ public static Props props (
73
+ final ActorSystem system ,
74
+ final java .time .Duration pollInterval ,
75
+ final MetricsFactory metricsFactory ) {
76
+ return Props .create (ClusterStatusCache .class , system , pollInterval , metricsFactory );
64
77
}
65
78
66
79
/**
67
80
* Public constructor.
68
81
*
69
- * @param cluster {@link akka.cluster.Cluster} whose state is cached
82
+ * @param system The Akka {@link ActorSystem}.
83
+ * @param pollInterval The {@link java.time.Duration} for polling state.
70
84
* @param metricsFactory A {@link MetricsFactory} to use for metrics creation.
71
85
*/
72
- public ClusterStatusCache (final Cluster cluster , final MetricsFactory metricsFactory ) {
73
- _cluster = cluster ;
86
+ public ClusterStatusCache (
87
+ final ActorSystem system ,
88
+ final java .time .Duration pollInterval ,
89
+ final MetricsFactory metricsFactory ) {
90
+ _cluster = Cluster .get (system );
91
+ _sharding = ClusterSharding .get (system );
92
+ _pollInterval = pollInterval ;
74
93
_metricsFactory = metricsFactory ;
75
94
}
76
95
@@ -81,7 +100,7 @@ public void preStart() {
81
100
.scheduler ();
82
101
_pollTimer = scheduler .schedule (
83
102
Duration .apply (0 , TimeUnit .SECONDS ),
84
- Duration .apply (10 , TimeUnit .SECONDS ),
103
+ Duration .apply (_pollInterval . toMillis () , TimeUnit .MILLISECONDS ),
85
104
getSelf (),
86
105
POLL ,
87
106
getContext ().system ().dispatcher (),
@@ -109,13 +128,50 @@ public Receive createReceive() {
109
128
}
110
129
}
111
130
})
131
+ .match (ShardRegion .ClusterShardingStats .class , shardingStats -> {
132
+ LOGGER .debug ()
133
+ .setMessage ("Received shard statistics" )
134
+ .addData ("regionCount" , shardingStats .getRegions ().size ())
135
+ .log ();
136
+ final Map <String , Integer > shardsPerAddress = Maps .newHashMap ();
137
+ final Map <String , Long > actorsPerAddress = Maps .newHashMap ();
138
+ for (final Map .Entry <Address , ShardRegion .ShardRegionStats > entry : shardingStats .getRegions ().entrySet ()) {
139
+ final String address = entry .getKey ().hostPort ();
140
+ shardsPerAddress .put (address , entry .getValue ().getStats ().size ());
141
+ for (final Object stat : entry .getValue ().getStats ().values ()) {
142
+ if (stat instanceof Number ) {
143
+ final long currentActorCount = actorsPerAddress .getOrDefault (address , 0L );
144
+ actorsPerAddress .put (
145
+ address ,
146
+ ((Number ) stat ).longValue () + currentActorCount );
147
+ }
148
+ }
149
+ }
150
+ for (final Map .Entry <String , Integer > entry : shardsPerAddress .entrySet ()) {
151
+ try (Metrics metrics = _metricsFactory .create ()) {
152
+ final Long actorCount = actorsPerAddress .get (entry .getKey ());
153
+ metrics .addAnnotation ("address" , entry .getKey ());
154
+ metrics .setGauge ("akka/cluster/shards" , entry .getValue ());
155
+ metrics .setGauge ("akka/cluster/actors" , actorCount );
156
+ }
157
+ }
158
+ })
112
159
.match (GetRequest .class , message -> sendResponse (getSender ()))
113
160
.match (ParallelLeastShardAllocationStrategy .RebalanceNotification .class , rebalanceNotification -> {
114
161
_rebalanceState = Optional .of (rebalanceNotification );
115
162
})
116
163
.matchEquals (POLL , message -> {
117
164
if (self ().equals (sender ())) {
118
165
_cluster .sendCurrentClusterState (getSelf ());
166
+ for (final String shardTypeName : _sharding .getShardTypeNames ()) {
167
+ LOGGER .debug ()
168
+ .setMessage ("Requesting shard statistics" )
169
+ .addData ("shardType" , shardTypeName )
170
+ .log ();
171
+ _sharding .shardRegion (shardTypeName ).tell (
172
+ new ShardRegion .GetClusterShardingStats (FiniteDuration .fromNanos (_pollInterval .toNanos ())),
173
+ self ());
174
+ }
119
175
} else {
120
176
unhandled (message );
121
177
}
@@ -131,7 +187,6 @@ private void sendResponse(final ActorRef sender) {
131
187
}
132
188
133
189
private static String hostFromActorRef (final ActorRef shardRegion ) {
134
-
135
190
return OptionConverters .toJava (
136
191
shardRegion .path ()
137
192
.address ()
@@ -140,13 +195,16 @@ private static String hostFromActorRef(final ActorRef shardRegion) {
140
195
}
141
196
142
197
private final Cluster _cluster ;
198
+ private final ClusterSharding _sharding ;
199
+ private final java .time .Duration _pollInterval ;
143
200
private final MetricsFactory _metricsFactory ;
144
201
private Optional <ClusterEvent .CurrentClusterState > _clusterState = Optional .empty ();
145
202
@ Nullable
146
203
private Cancellable _pollTimer ;
147
204
private Optional <ParallelLeastShardAllocationStrategy .RebalanceNotification > _rebalanceState = Optional .empty ();
148
205
149
206
private static final String POLL = "poll" ;
207
+ private static final Logger LOGGER = LoggerFactory .getLogger (ClusterStatusCache .class );
150
208
151
209
/**
152
210
* Request to get a cluster status.
0 commit comments