/// <summary> /// Receives changes from peer nodes, merges remote with local gossip nodes, then publishes /// changes to the event stream for load balancing router consumption, and gossip back. /// </summary> private void ReceiveGossip(MetricsGossipEnvelope envelope) { // remote node might not have same view of member nodes, this side should only care // about nodes that are known here, otherwise removed nodes can come back var otherGossip = envelope.Gossip.Filter(_nodes.Select(n => n).ToImmutableHashSet()); _latestGossip = _latestGossip.Merge(otherGossip); // changes will be published in the period collect task if (!envelope.Reply) { ReplyGossipTo(envelope.FromAddress); } }
private Msg.MetricsGossipEnvelope MetricsGossipEnvelopeToProto(MetricsGossipEnvelope envelope) { var mgossip = envelope.Gossip; var allAddresses = mgossip.Nodes.Select(x => x.Address).ToList(); var addressMapping = allAddresses.ZipWithIndex(); var allMetricNames = mgossip.Nodes.Aggregate(ImmutableHashSet.Create <string>(), (set, metrics) => set = set.Union(metrics.Metrics.Select(x => x.Name))); var metricNamesMapping = allMetricNames.ZipWithIndex(); Func <Address, int> mapAddress = address => MapWithErrorMessage(addressMapping, address, "address"); Func <string, int> mapName = name => MapWithErrorMessage(metricNamesMapping, name, "address"); Func <EWMA, Msg.NodeMetrics.Types.EWMA.Builder> ewmaToProto = ewma => ewma == null ? null : Msg.NodeMetrics.Types.EWMA.CreateBuilder().SetAlpha(ewma.Alpha).SetValue(ewma.Value); // we set all metric types as doubles, since we don't have a convenient Number base class like Scala Func <double, Msg.NodeMetrics.Types.Number.Builder> numberToProto = d => Msg.NodeMetrics.Types.Number.CreateBuilder() .SetType(Msg.NodeMetrics.Types.NumberType.Double) .SetValue64((ulong)BitConverter.DoubleToInt64Bits(d)); Func <Metric, Msg.NodeMetrics.Types.Metric.Builder> metricToProto = metric => { var builder = Msg.NodeMetrics.Types.Metric.CreateBuilder() .SetNameIndex(mapName(metric.Name)) .SetNumber(numberToProto(metric.Value)); var ewmaBuilder = ewmaToProto(metric.Average); return(ewmaBuilder != null?builder.SetEwma(ewmaBuilder) : builder); }; Func <NodeMetrics, Msg.NodeMetrics.Builder> nodeMetricsToProto = metrics => Msg.NodeMetrics.CreateBuilder() .SetAddressIndex(mapAddress(metrics.Address)) .SetTimestamp(metrics.Timestamp) .AddRangeMetrics(metrics.Metrics.Select(x => metricToProto(x).Build())); var nodeMetrics = mgossip.Nodes.Select(x => nodeMetricsToProto(x).Build()); return(Msg.MetricsGossipEnvelope.CreateBuilder().SetFrom(AddressToProto(envelope.From)).SetGossip( Msg.MetricsGossip.CreateBuilder() .AddRangeAllAddresses(allAddresses.Select(x => AddressToProto(x).Build())) .AddRangeAllMetricNames(allMetricNames).AddRangeNodeMetrics(nodeMetrics)) .SetReply(envelope.Reply) .Build()); }
private void SendGossip(Akka.Actor.Address address, MetricsGossipEnvelope envelope) { Context.ActorSelection(Self.Path.ToStringWithAddress(address)).Tell(envelope); }