Esempio n. 1
0
        /// <summary>
        /// Message-processing behavior when the <see cref="EndpointManager"/> is able to accept
        /// inbound association requests.
        /// </summary>
        protected void Accepting()
        {
            Receive <ManagementCommand>(mc =>
            {
                /*
                 * applies a management command to all available transports.
                 *
                 * Useful for things like global restart
                 */
                var sender      = Sender;
                var allStatuses = _transportMapping.Values.Select(x => x.ManagementCommand(mc.Cmd));
                Task.WhenAll(allStatuses)
                .ContinueWith(x =>
                {
                    return(new ManagementCommandAck(x.Result.All(y => y)));
                }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
                .PipeTo(sender);
            });

            Receive <Quarantine>(quarantine =>
            {
                //Stop writers
                var policy =
                    Tuple.Create(_endpoints.WritableEndpointWithPolicyFor(quarantine.RemoteAddress), quarantine.Uid);
                if (policy.Item1 is Pass && policy.Item2 == null)
                {
                    var endpoint = policy.Item1.AsInstanceOf <Pass>().Endpoint;
                    Context.Stop(endpoint);
                    _log.Warning("Association to [{0}] with unknown UID is reported as quarantined, but " +
                                 "address cannot be quarantined without knowing the UID, gating instead for {1} ms.", quarantine.RemoteAddress, _settings.RetryGateClosedFor.TotalMilliseconds);
                    _endpoints.MarkAsFailed(endpoint, Deadline.Now + _settings.RetryGateClosedFor);
                }
                else if (policy.Item1 is Pass && policy.Item2 != null)
                {
                    var pass          = (Pass)policy.Item1;
                    var uidOption     = pass.Uid;
                    var quarantineUid = policy.Item2;
                    if (uidOption == quarantineUid)
                    {
                        _endpoints.MarkAsQuarantined(quarantine.RemoteAddress, quarantineUid.Value, Deadline.Now + _settings.QuarantineDuration);
                        _eventPublisher.NotifyListeners(new QuarantinedEvent(quarantine.RemoteAddress, quarantineUid.Value));
                        Context.Stop(pass.Endpoint);
                    }
                    // or it does not match with the UID to be quarantined
                    else if (!uidOption.HasValue && pass.RefuseUid != quarantineUid)
                    {
                        // the quarantine uid may be got fresh by cluster gossip, so update refuseUid for late handle when the writer got uid
                        _endpoints.RegisterWritableEndpointRefuseUid(quarantine.RemoteAddress, quarantineUid.Value);
                    }
                    else
                    {
                        //the quarantine uid has lost the race with some failure, do nothing
                    }
                }
                else if (policy.Item1 is WasGated && policy.Item2 != null)
                {
                    var wg = (WasGated)policy.Item1;
                    if (wg.RefuseUid == policy.Item2)
                    {
                        _endpoints.RegisterWritableEndpointRefuseUid(quarantine.RemoteAddress, policy.Item2.Value);
                    }
                }
                else if (policy.Item1 is Quarantined && policy.Item2 != null && policy.Item1.AsInstanceOf <Quarantined>().Uid == policy.Item2.Value)
                {
                    // the UID to be quarantined already exists, do nothing
                }
                else if (policy.Item2 != null)
                {
                    // the current state is gated or quarantined, and we know the UID, update
                    _endpoints.MarkAsQuarantined(quarantine.RemoteAddress, policy.Item2.Value, Deadline.Now + _settings.QuarantineDuration);
                    _eventPublisher.NotifyListeners(new QuarantinedEvent(quarantine.RemoteAddress, policy.Item2.Value));
                }
                else
                {
                    // the current state is Gated, WasGated, or Quarantined and we don't know the UID, do nothing.
                }

                // Stop inbound read-only associations
                var readPolicy = Tuple.Create(_endpoints.ReadOnlyEndpointFor(quarantine.RemoteAddress), quarantine.Uid);
                if (readPolicy.Item1?.Item1 != null && quarantine.Uid == null)
                {
                    Context.Stop(readPolicy.Item1.Item1);
                }
                else if (readPolicy.Item1?.Item1 != null && quarantine.Uid != null && readPolicy.Item1?.Item2 == quarantine.Uid)
                {
                    Context.Stop(readPolicy.Item1.Item1);
                }
                else
                {
                }        // nothing to stop

                Func <AkkaProtocolHandle, bool> matchesQuarantine = handle => handle.RemoteAddress.Equals(quarantine.RemoteAddress) &&
                                                                    quarantine.Uid == handle.HandshakeInfo.Uid;

                // Stop all matching pending read handoffs
                _pendingReadHandoffs = _pendingReadHandoffs.Where(x =>
                {
                    var drop = matchesQuarantine(x.Value);
                    // Side-effecting here
                    if (drop)
                    {
                        x.Value.Disassociate();
                        Context.Stop(x.Key);
                    }
                    return(!drop);
                }).ToDictionary(key => key.Key, value => value.Value);

                // Stop all matching stashed connections
                _stashedInbound = _stashedInbound.Select(x =>
                {
                    var associations = x.Value.Where(assoc =>
                    {
                        var handle = assoc.Association.AsInstanceOf <AkkaProtocolHandle>();
                        var drop   = matchesQuarantine(handle);
                        if (drop)
                        {
                            handle.Disassociate();
                        }
                        return(!drop);
                    }).ToList();
                    return(new KeyValuePair <IActorRef, List <InboundAssociation> >(x.Key, associations));
                }).ToDictionary(k => k.Key, v => v.Value);
            });

            Receive <Send>(send =>
            {
                var recipientAddress = send.Recipient.Path.Address;
                Func <int?, IActorRef> createAndRegisterWritingEndpoint = refuseUid => _endpoints.RegisterWritableEndpoint(recipientAddress,
                                                                                                                           CreateEndpoint(recipientAddress, send.Recipient.LocalAddressToUse,
                                                                                                                                          _transportMapping[send.Recipient.LocalAddressToUse], _settings, writing: true,
                                                                                                                                          handleOption: null, refuseUid: refuseUid), uid: null, refuseUid: refuseUid);

                // pattern match won't throw a NullReferenceException if one is returned by WritableEndpointWithPolicyFor
                _endpoints.WritableEndpointWithPolicyFor(recipientAddress).Match()
                .With <Pass>(
                    pass =>
                {
                    pass.Endpoint.Tell(send);
                })
                .With <Gated>(gated =>
                {
                    if (gated.TimeOfRelease.IsOverdue)
                    {
                        createAndRegisterWritingEndpoint(gated.RefuseUid).Tell(send);
                    }
                    else
                    {
                        Context.System.DeadLetters.Tell(send);
                    }
                })
                .With <WasGated>(wasGated =>
                {
                    createAndRegisterWritingEndpoint(wasGated.RefuseUid).Tell(send);
                })
                .With <Quarantined>(quarantined =>
                {
                    // timeOfRelease is only used for garbage collection reasons, therefore it is ignored here. We still have
                    // the Quarantined tombstone and we know what UID we don't want to accept, so use it.
                    createAndRegisterWritingEndpoint(quarantined.Uid).Tell(send);
                })
                .Default(msg => createAndRegisterWritingEndpoint(null).Tell(send));
            });
            Receive <InboundAssociation>(ia => HandleInboundAssociation(ia, false));
            Receive <EndpointWriter.StoppedReading>(endpoint => AcceptPendingReader(endpoint.Writer));
            Receive <Terminated>(terminated =>
            {
                AcceptPendingReader(terminated.ActorRef);
                _endpoints.UnregisterEndpoint(terminated.ActorRef);
                HandleStashedInbound(terminated.ActorRef, writerIsIdle: false);
            });
            Receive <EndpointWriter.TookOver>(tookover => RemovePendingReader(tookover.Writer, tookover.ProtocolHandle));
            Receive <ReliableDeliverySupervisor.GotUid>(gotuid =>
            {
                var policy = _endpoints.WritableEndpointWithPolicyFor(gotuid.RemoteAddress);
                var pass   = policy as Pass;
                if (pass != null)
                {
                    if (pass.RefuseUid == gotuid.Uid)
                    {
                        _endpoints.MarkAsQuarantined(gotuid.RemoteAddress, gotuid.Uid,
                                                     Deadline.Now + _settings.QuarantineDuration);
                        _eventPublisher.NotifyListeners(new QuarantinedEvent(gotuid.RemoteAddress, gotuid.Uid));
                        Context.Stop(pass.Endpoint);
                    }
                    else
                    {
                        _endpoints.RegisterWritableEndpointUid(gotuid.RemoteAddress, gotuid.Uid);
                    }
                    HandleStashedInbound(Sender, writerIsIdle: false);
                }
                else if (policy is WasGated)
                {
                    var wg = (WasGated)policy;
                    if (wg.RefuseUid == gotuid.Uid)
                    {
                        _endpoints.MarkAsQuarantined(gotuid.RemoteAddress, gotuid.Uid,
                                                     Deadline.Now + _settings.QuarantineDuration);
                        _eventPublisher.NotifyListeners(new QuarantinedEvent(gotuid.RemoteAddress, gotuid.Uid));
                        Context.Stop(pass.Endpoint);
                    }
                    else
                    {
                        _endpoints.RegisterWritableEndpointUid(gotuid.RemoteAddress, gotuid.Uid);
                    }
                    HandleStashedInbound(Sender, writerIsIdle: false);
                }
                else
                {
                    // the GotUid might have lost the race with some failure
                }
            });
            Receive <ReliableDeliverySupervisor.Idle>(idle =>
            {
                HandleStashedInbound(Sender, writerIsIdle: true);
            });
            Receive <Prune>(prune => _endpoints.Prune());
            Receive <ShutdownAndFlush>(shutdown =>
            {
                //Shutdown all endpoints and signal to Sender when ready (and whether all endpoints were shutdown gracefully)
                var sender = Sender;

                // The construction of the Task for shutdownStatus has to happen after the flushStatus future has been finished
                // so that endpoints are shut down before transports.
                var shutdownStatus = Task.WhenAll(_endpoints.AllEndpoints.Select(
                                                      x => x.GracefulStop(_settings.FlushWait, EndpointWriter.FlushAndStop.Instance))).ContinueWith(
                    result =>
                {
                    if (result.IsFaulted || result.IsCanceled)
                    {
                        if (result.Exception != null)
                        {
                            result.Exception.Handle(e => true);
                        }
                        return(false);
                    }
                    return(result.Result.All(x => x));
                }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

                shutdownStatus.ContinueWith(tr => Task.WhenAll(_transportMapping.Values.Select(x => x.Shutdown())).ContinueWith(
                                                result =>
                {
                    if (result.IsFaulted || result.IsCanceled)
                    {
                        if (result.Exception != null)
                        {
                            result.Exception.Handle(e => true);
                        }
                        return(false);
                    }
                    return(result.Result.All(x => x) && tr.Result);
                }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)).Unwrap().PipeTo(sender);


                foreach (var handoff in _pendingReadHandoffs.Values)
                {
                    handoff.Disassociate(DisassociateInfo.Shutdown);
                }

                //Ignore all other writes
                _normalShutdown = true;
                Become(Flushing);
            });
        }