protected void Accepting(object message) { message.Match() .With <ManagementCommand>(mc => { var allStatuses = _transportMapping.Values.Select(x => x.ManagementCommand(mc)); Task.WhenAll(allStatuses) .ContinueWith(x => new ManagementCommandAck(x.Result.All(y => y)), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.AttachedToParent) .PipeTo(Self); }) .With <Quarantine>(quarantine => { //Stop writers if (endpoints.WritableEndpointWithPolicyFor(quarantine.RemoteAddress) is Pass) { var pass = (Pass)endpoints.WritableEndpointWithPolicyFor(quarantine.RemoteAddress); Context.Stop(pass.Endpoint); if (!pass.Uid.HasValue) { log.Warning("Association to [{0}] with unknown UID is reported as quarantined, but " + "address cannot be quarantined without knowing the UID, gated instead for {0} ms", quarantine.RemoteAddress, settings.RetryGateClosedFor.TotalMilliseconds); endpoints.MarkAsFailed(pass.Endpoint, Deadline.Now + settings.RetryGateClosedFor); } } //Stop inbound read-only association var read = endpoints.ReadOnlyEndpointFor(quarantine.RemoteAddress); if (read != null) { Context.Stop((InternalActorRef)read); } if (quarantine.Uid.HasValue) { endpoints.MarkAsQuarantined(quarantine.RemoteAddress, quarantine.Uid.Value, Deadline.Now + settings.QuarantineDuration); eventPublisher.NotifyListeners(new QuarantinedEvent(quarantine.RemoteAddress, quarantine.Uid.Value)); } }) .With <Send>(send => { var recipientAddress = send.Recipient.Path.Address; Func <int?, ActorRef> createAndRegisterWritingEndpoint = refuseUid => endpoints.RegisterWritableEndpoint(recipientAddress, CreateEndpoint(recipientAddress, send.Recipient.LocalAddressToUse, _transportMapping[send.Recipient.LocalAddressToUse], settings, writing: true, handleOption: null, refuseUid: refuseUid), refuseUid); endpoints.WritableEndpointWithPolicyFor(recipientAddress).Match() .With <Pass>( pass => { pass.Endpoint.Tell(send); }) .With <Gated>(gated => { if (gated.TimeOfRelease.IsOverdue) { createAndRegisterWritingEndpoint(null).Tell(send); } else { Context.System.DeadLetters.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((int)quarantined.Uid).Tell(send); }) .Default(msg => createAndRegisterWritingEndpoint(null).Tell(send)); }) .With <InboundAssociation>(HandleInboundAssociation) .With <EndpointWriter.StoppedReading>(endpoint => AcceptPendingReader(endpoint.Writer)) .With <Terminated>(terminated => { AcceptPendingReader(terminated.ActorRef); endpoints.UnregisterEndpoint(terminated.ActorRef); HandleStashedInbound(terminated.ActorRef); }) .With <EndpointWriter.TookOver>(tookover => RemovePendingReader(tookover.Writer, tookover.ProtocolHandle)) .With <ReliableDeliverySupervisor.GotUid>(gotuid => { endpoints.RegisterWritableEndpointUid(Sender, gotuid.Uid); HandleStashedInbound(Sender); }) .With <Prune>(prune => endpoints.Prune()) .With <ShutdownAndFlush>(shutdown => { //Shutdown all endpoints and signal to Sender when ready (and whether all endpoints were shutdown gracefully) // 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, new EndpointWriter.FlushAndStop()))).ContinueWith( result => { if (result.IsFaulted) { if (result.Exception != null) { result.Exception.Handle(e => true); } return(false); } return(result.Result.All(x => x)); }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.AttachedToParent); var flushStatus = Task.WhenAll(_transportMapping.Values.Select(x => x.Shutdown())).ContinueWith( result => { if (result.IsFaulted) { if (result.Exception != null) { result.Exception.Handle(e => true); } return(false); } return(result.Result.All(x => x)); }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.AttachedToParent); Task.WhenAll(shutdownStatus, flushStatus) .ContinueWith(x => x.Result.All(y => y), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.AttachedToParent) .PipeTo(Sender); foreach (var handoff in pendingReadHandoffs.Values) { handoff.Disassociate(DisassociateInfo.Shutdown); } //Ignore all other writes Context.Become(Flushing); }); }
/// <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 = policy.Item1 as Pass; if (pass.Uid == quarantine.Uid) { Context.Stop(pass.Endpoint); } } else { // Do nothing, because either: // A: we don't know yet the UID of the writer, it will be checked against current quarantine state later // B: we know the UID, but it does not match with the UID to be quarantined } // 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); if (quarantine.Uid.HasValue) { _endpoints.MarkAsQuarantined(quarantine.RemoteAddress, quarantine.Uid.Value, Deadline.Now + _settings.QuarantineDuration); _eventPublisher.NotifyListeners(new QuarantinedEvent(quarantine.RemoteAddress, quarantine.Uid.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); _endpoints.WritableEndpointWithPolicyFor(recipientAddress).Match() .With <Pass>( pass => { pass.Endpoint.Tell(send); }) .With <Gated>(gated => { if (gated.TimeOfRelease.IsOverdue) { createAndRegisterWritingEndpoint(null).Tell(send); } else { Context.System.DeadLetters.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 => { _endpoints.RegisterWritableEndpointUid(Sender, gotuid.Uid); HandleStashedInbound(Sender, writerIsIdle: false); }); 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); }); }