2016-05-09 07:31:41 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2016 Lightbend Inc. <http://www.lightbend.com>
|
|
|
|
|
*/
|
|
|
|
|
package akka.remote.artery
|
|
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
import java.io.File
|
|
|
|
|
import java.net.InetSocketAddress
|
|
|
|
|
import java.nio.channels.{ DatagramChannel, FileChannel }
|
2016-06-02 07:21:32 +02:00
|
|
|
import java.util.concurrent.CopyOnWriteArrayList
|
2016-06-10 13:04:23 +02:00
|
|
|
import java.util.concurrent.TimeUnit
|
2016-09-02 16:40:38 +02:00
|
|
|
import java.util.concurrent.atomic.{ AtomicLong, AtomicReference }
|
2016-07-04 16:42:14 +02:00
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
import scala.annotation.tailrec
|
|
|
|
|
import scala.collection.JavaConverters._
|
2016-05-09 07:31:41 +02:00
|
|
|
import scala.concurrent.Future
|
2016-05-13 08:06:13 +02:00
|
|
|
import scala.concurrent.Promise
|
2016-05-09 07:31:41 +02:00
|
|
|
import scala.concurrent.duration._
|
2016-05-13 08:06:13 +02:00
|
|
|
import scala.util.Failure
|
|
|
|
|
import scala.util.Success
|
2016-05-17 17:34:57 +02:00
|
|
|
import scala.util.Try
|
2016-08-24 19:52:07 +02:00
|
|
|
import scala.util.control.NonFatal
|
2016-05-09 07:31:41 +02:00
|
|
|
import akka.Done
|
|
|
|
|
import akka.NotUsed
|
2016-06-23 11:58:54 +02:00
|
|
|
import akka.actor._
|
2016-08-24 19:52:07 +02:00
|
|
|
import akka.actor.Actor
|
2016-06-02 07:21:32 +02:00
|
|
|
import akka.actor.Cancellable
|
2016-08-24 19:52:07 +02:00
|
|
|
import akka.actor.Props
|
2016-05-09 07:31:41 +02:00
|
|
|
import akka.event.Logging
|
|
|
|
|
import akka.event.LoggingAdapter
|
|
|
|
|
import akka.remote.AddressUidExtension
|
2016-05-13 15:34:37 +02:00
|
|
|
import akka.remote.EventPublisher
|
2016-05-09 07:31:41 +02:00
|
|
|
import akka.remote.RemoteActorRef
|
|
|
|
|
import akka.remote.RemoteActorRefProvider
|
|
|
|
|
import akka.remote.RemoteTransport
|
2016-05-13 15:34:37 +02:00
|
|
|
import akka.remote.RemotingLifecycleEvent
|
|
|
|
|
import akka.remote.ThisActorSystemQuarantinedEvent
|
2016-05-09 07:31:41 +02:00
|
|
|
import akka.remote.UniqueAddress
|
2016-08-24 19:52:07 +02:00
|
|
|
import akka.remote.artery.Encoder.ChangeOutboundCompression
|
2016-05-13 15:34:37 +02:00
|
|
|
import akka.remote.artery.InboundControlJunction.ControlMessageObserver
|
2016-05-12 08:56:28 +02:00
|
|
|
import akka.remote.artery.InboundControlJunction.ControlMessageSubject
|
2016-08-24 19:52:07 +02:00
|
|
|
import akka.remote.artery.OutboundControlJunction.OutboundControlIngress
|
|
|
|
|
import akka.remote.artery.compress._
|
|
|
|
|
import akka.remote.artery.compress.CompressionProtocol.CompressionMessage
|
2016-05-09 07:31:41 +02:00
|
|
|
import akka.remote.transport.AkkaPduCodec
|
|
|
|
|
import akka.remote.transport.AkkaPduProtobufCodec
|
2016-05-17 17:34:57 +02:00
|
|
|
import akka.stream.AbruptTerminationException
|
2016-05-09 07:31:41 +02:00
|
|
|
import akka.stream.ActorMaterializer
|
2016-08-24 19:52:07 +02:00
|
|
|
import akka.stream.ActorMaterializerSettings
|
2016-05-09 07:31:41 +02:00
|
|
|
import akka.stream.KillSwitches
|
|
|
|
|
import akka.stream.Materializer
|
|
|
|
|
import akka.stream.SharedKillSwitch
|
|
|
|
|
import akka.stream.scaladsl.Flow
|
|
|
|
|
import akka.stream.scaladsl.Keep
|
|
|
|
|
import akka.stream.scaladsl.Sink
|
|
|
|
|
import akka.stream.scaladsl.Source
|
2016-05-17 17:34:57 +02:00
|
|
|
import akka.util.Helpers.ConfigOps
|
|
|
|
|
import akka.util.Helpers.Requiring
|
2016-08-24 19:52:07 +02:00
|
|
|
import akka.util.OptionVal
|
2016-08-23 20:38:39 +02:00
|
|
|
import akka.util.WildcardIndex
|
2016-05-09 07:31:41 +02:00
|
|
|
import io.aeron.Aeron
|
|
|
|
|
import io.aeron.AvailableImageHandler
|
2016-08-24 19:52:07 +02:00
|
|
|
import io.aeron.CncFileDescriptor
|
2016-05-09 07:31:41 +02:00
|
|
|
import io.aeron.Image
|
|
|
|
|
import io.aeron.UnavailableImageHandler
|
|
|
|
|
import io.aeron.driver.MediaDriver
|
2016-08-24 19:52:07 +02:00
|
|
|
import io.aeron.driver.ThreadingMode
|
2016-05-09 07:31:41 +02:00
|
|
|
import io.aeron.exceptions.ConductorServiceTimeoutException
|
|
|
|
|
import org.agrona.ErrorHandler
|
|
|
|
|
import org.agrona.IoUtil
|
2016-06-10 07:41:36 +02:00
|
|
|
import org.agrona.concurrent.BackoffIdleStrategy
|
2016-08-30 14:37:11 +02:00
|
|
|
import akka.stream.scaladsl.BroadcastHub
|
2016-09-07 08:27:33 +02:00
|
|
|
import scala.util.control.NoStackTrace
|
2016-09-06 14:32:42 +02:00
|
|
|
import io.aeron.exceptions.DriverTimeoutException
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
2016-06-06 08:26:15 +02:00
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
* Inbound API that is used by the stream stages.
|
|
|
|
|
* Separate trait to facilitate testing without real transport.
|
|
|
|
|
*/
|
|
|
|
|
private[akka] trait InboundContext {
|
|
|
|
|
/**
|
|
|
|
|
* The local inbound address.
|
|
|
|
|
*/
|
|
|
|
|
def localAddress: UniqueAddress
|
|
|
|
|
|
|
|
|
|
/**
|
2016-05-12 08:56:28 +02:00
|
|
|
* An inbound stage can send control message, e.g. a reply, to the origin
|
2016-05-13 08:06:13 +02:00
|
|
|
* address with this method. It will be sent over the control sub-channel.
|
2016-05-09 07:31:41 +02:00
|
|
|
*/
|
2016-05-12 08:56:28 +02:00
|
|
|
def sendControl(to: Address, message: ControlMessage): Unit
|
2016-05-09 07:31:41 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Lookup the outbound association for a given address.
|
|
|
|
|
*/
|
|
|
|
|
def association(remoteAddress: Address): OutboundContext
|
2016-05-13 15:34:37 +02:00
|
|
|
|
2016-05-25 12:28:44 +02:00
|
|
|
/**
|
|
|
|
|
* Lookup the outbound association for a given UID.
|
2016-06-05 15:40:06 +02:00
|
|
|
* Will return `OptionVal.None` if the UID is unknown, i.e.
|
|
|
|
|
* handshake not completed.
|
2016-05-25 12:28:44 +02:00
|
|
|
*/
|
2016-06-05 15:40:06 +02:00
|
|
|
def association(uid: Long): OptionVal[OutboundContext]
|
2016-05-25 12:28:44 +02:00
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
def completeHandshake(peer: UniqueAddress): Future[Done]
|
2016-05-25 12:28:44 +02:00
|
|
|
|
2016-09-07 10:41:36 +02:00
|
|
|
def settings: ArterySettings
|
|
|
|
|
|
2016-05-13 15:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[akka] object AssociationState {
|
|
|
|
|
def apply(): AssociationState =
|
2016-06-23 11:58:54 +02:00
|
|
|
new AssociationState(
|
|
|
|
|
incarnation = 1,
|
|
|
|
|
uniqueRemoteAddressPromise = Promise(),
|
2016-08-24 19:52:07 +02:00
|
|
|
quarantined = ImmutableLongMap.empty[QuarantinedTimestamp])
|
2016-06-04 21:53:27 +02:00
|
|
|
|
2016-06-10 13:04:23 +02:00
|
|
|
final case class QuarantinedTimestamp(nanoTime: Long) {
|
|
|
|
|
override def toString: String =
|
|
|
|
|
s"Quarantined ${TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - nanoTime)} seconds ago"
|
2016-06-04 21:53:27 +02:00
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-13 15:34:37 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[akka] final class AssociationState(
|
2016-09-01 09:07:39 +03:00
|
|
|
val incarnation: Int,
|
2016-05-13 15:34:37 +02:00
|
|
|
val uniqueRemoteAddressPromise: Promise[UniqueAddress],
|
2016-09-01 09:07:39 +03:00
|
|
|
val quarantined: ImmutableLongMap[AssociationState.QuarantinedTimestamp]) {
|
2016-06-10 13:04:23 +02:00
|
|
|
|
|
|
|
|
import AssociationState.QuarantinedTimestamp
|
2016-05-13 08:06:13 +02:00
|
|
|
|
2016-06-04 22:14:28 +02:00
|
|
|
// doesn't have to be volatile since it's only a cache changed once
|
|
|
|
|
private var uniqueRemoteAddressValueCache: Option[UniqueAddress] = null
|
|
|
|
|
|
2016-05-13 08:06:13 +02:00
|
|
|
/**
|
|
|
|
|
* Full outbound address with UID for this association.
|
|
|
|
|
* Completed when by the handshake.
|
|
|
|
|
*/
|
|
|
|
|
def uniqueRemoteAddress: Future[UniqueAddress] = uniqueRemoteAddressPromise.future
|
|
|
|
|
|
2016-06-04 22:14:28 +02:00
|
|
|
def uniqueRemoteAddressValue(): Option[UniqueAddress] = {
|
|
|
|
|
if (uniqueRemoteAddressValueCache ne null)
|
|
|
|
|
uniqueRemoteAddressValueCache
|
|
|
|
|
else {
|
|
|
|
|
uniqueRemoteAddress.value match {
|
|
|
|
|
case Some(Success(peer)) ⇒
|
|
|
|
|
uniqueRemoteAddressValueCache = Some(peer)
|
|
|
|
|
uniqueRemoteAddressValueCache
|
|
|
|
|
case _ ⇒ None
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-13 15:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
def newIncarnation(remoteAddressPromise: Promise[UniqueAddress]): AssociationState =
|
|
|
|
|
new AssociationState(incarnation + 1, remoteAddressPromise, quarantined)
|
2016-05-13 15:34:37 +02:00
|
|
|
|
|
|
|
|
def newQuarantined(): AssociationState =
|
|
|
|
|
uniqueRemoteAddressPromise.future.value match {
|
|
|
|
|
case Some(Success(a)) ⇒
|
2016-06-23 11:58:54 +02:00
|
|
|
new AssociationState(
|
|
|
|
|
incarnation,
|
|
|
|
|
uniqueRemoteAddressPromise,
|
2016-08-24 19:52:07 +02:00
|
|
|
quarantined = quarantined.updated(a.uid, QuarantinedTimestamp(System.nanoTime())))
|
2016-05-13 15:34:37 +02:00
|
|
|
case _ ⇒ this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def isQuarantined(): Boolean = {
|
|
|
|
|
uniqueRemoteAddressValue match {
|
2016-06-04 22:14:28 +02:00
|
|
|
case Some(a) ⇒ isQuarantined(a.uid)
|
|
|
|
|
case _ ⇒ false // handshake not completed yet
|
2016-05-13 15:34:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-10 13:04:23 +02:00
|
|
|
def isQuarantined(uid: Long): Boolean = quarantined.contains(uid)
|
2016-05-13 15:34:37 +02:00
|
|
|
|
2016-05-13 08:06:13 +02:00
|
|
|
override def toString(): String = {
|
|
|
|
|
val a = uniqueRemoteAddressPromise.future.value match {
|
|
|
|
|
case Some(Success(a)) ⇒ a
|
|
|
|
|
case Some(Failure(e)) ⇒ s"Failure(${e.getMessage})"
|
|
|
|
|
case None ⇒ "unknown"
|
|
|
|
|
}
|
|
|
|
|
s"AssociationState($incarnation, $a)"
|
|
|
|
|
}
|
2016-05-17 17:34:57 +02:00
|
|
|
|
2016-05-13 08:06:13 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
* Outbound association API that is used by the stream stages.
|
|
|
|
|
* Separate trait to facilitate testing without real transport.
|
|
|
|
|
*/
|
|
|
|
|
private[akka] trait OutboundContext {
|
|
|
|
|
/**
|
|
|
|
|
* The local inbound address.
|
|
|
|
|
*/
|
|
|
|
|
def localAddress: UniqueAddress
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The outbound address for this association.
|
|
|
|
|
*/
|
|
|
|
|
def remoteAddress: Address
|
|
|
|
|
|
2016-05-13 08:06:13 +02:00
|
|
|
def associationState: AssociationState
|
|
|
|
|
|
2016-05-13 15:34:37 +02:00
|
|
|
def quarantine(reason: String): Unit
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
/**
|
2016-05-13 08:06:13 +02:00
|
|
|
* An inbound stage can send control message, e.g. a HandshakeReq, to the remote
|
|
|
|
|
* address of this association. It will be sent over the control sub-channel.
|
2016-05-09 07:31:41 +02:00
|
|
|
*/
|
2016-05-13 08:06:13 +02:00
|
|
|
def sendControl(message: ControlMessage): Unit
|
2016-05-09 07:31:41 +02:00
|
|
|
|
|
|
|
|
/**
|
2016-05-12 08:56:28 +02:00
|
|
|
* An outbound stage can listen to control messages
|
2016-05-09 07:31:41 +02:00
|
|
|
* via this observer subject.
|
|
|
|
|
*/
|
2016-05-12 08:56:28 +02:00
|
|
|
def controlSubject: ControlMessageSubject
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-09-07 10:41:36 +02:00
|
|
|
def settings: ArterySettings
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-23 18:11:56 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[remote] object FlushOnShutdown {
|
|
|
|
|
def props(done: Promise[Done], timeout: FiniteDuration,
|
2016-09-01 09:07:39 +03:00
|
|
|
inboundContext: InboundContext, associations: Set[Association]): Props = {
|
2016-06-23 18:11:56 +02:00
|
|
|
require(associations.nonEmpty)
|
|
|
|
|
Props(new FlushOnShutdown(done, timeout, inboundContext, associations))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case object Timeout
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[remote] class FlushOnShutdown(done: Promise[Done], timeout: FiniteDuration,
|
2016-09-01 09:07:39 +03:00
|
|
|
inboundContext: InboundContext, associations: Set[Association]) extends Actor {
|
2016-06-23 18:11:56 +02:00
|
|
|
|
|
|
|
|
var remaining = associations.flatMap(_.associationState.uniqueRemoteAddressValue)
|
|
|
|
|
|
|
|
|
|
val timeoutTask = context.system.scheduler.scheduleOnce(timeout, self, FlushOnShutdown.Timeout)(context.dispatcher)
|
|
|
|
|
|
|
|
|
|
override def preStart(): Unit = {
|
2016-09-07 08:27:33 +02:00
|
|
|
// FIXME shall we also try to flush the ordinary message stream, not only control stream?
|
2016-06-23 18:11:56 +02:00
|
|
|
val msg = ActorSystemTerminating(inboundContext.localAddress)
|
2016-09-06 14:32:42 +02:00
|
|
|
try {
|
|
|
|
|
associations.foreach { a ⇒ a.send(msg, OptionVal.Some(self), OptionVal.None) }
|
|
|
|
|
} catch {
|
|
|
|
|
case NonFatal(e) ⇒
|
|
|
|
|
// send may throw
|
|
|
|
|
done.tryFailure(e)
|
|
|
|
|
throw e
|
|
|
|
|
}
|
2016-06-23 18:11:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:42 +02:00
|
|
|
override def postStop(): Unit = {
|
2016-06-23 18:11:56 +02:00
|
|
|
timeoutTask.cancel()
|
2016-09-06 14:32:42 +02:00
|
|
|
done.trySuccess(Done)
|
|
|
|
|
}
|
2016-06-23 18:11:56 +02:00
|
|
|
|
|
|
|
|
def receive = {
|
|
|
|
|
case ActorSystemTerminatingAck(from) ⇒
|
|
|
|
|
remaining -= from
|
2016-09-06 14:32:42 +02:00
|
|
|
if (remaining.isEmpty)
|
2016-06-23 18:11:56 +02:00
|
|
|
context.stop(self)
|
|
|
|
|
case FlushOnShutdown.Timeout ⇒
|
|
|
|
|
context.stop(self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[remote] class ArteryTransport(_system: ExtendedActorSystem, _provider: RemoteActorRefProvider)
|
|
|
|
|
extends RemoteTransport(_system, _provider) with InboundContext {
|
2016-06-06 13:36:05 +02:00
|
|
|
import FlightRecorderEvents._
|
2016-09-07 08:27:33 +02:00
|
|
|
import ArteryTransport.ShutdownSignal
|
2016-09-06 14:32:42 +02:00
|
|
|
import ArteryTransport.AeronTerminated
|
2016-05-09 07:31:41 +02:00
|
|
|
|
|
|
|
|
// these vars are initialized once in the start method
|
|
|
|
|
@volatile private[this] var _localAddress: UniqueAddress = _
|
2016-05-27 16:45:48 +02:00
|
|
|
@volatile private[this] var _addresses: Set[Address] = _
|
2016-05-09 07:31:41 +02:00
|
|
|
@volatile private[this] var materializer: Materializer = _
|
2016-05-12 08:56:28 +02:00
|
|
|
@volatile private[this] var controlSubject: ControlMessageSubject = _
|
2016-05-09 07:31:41 +02:00
|
|
|
@volatile private[this] var messageDispatcher: MessageDispatcher = _
|
2016-09-02 16:40:38 +02:00
|
|
|
private[this] val mediaDriver = new AtomicReference[Option[MediaDriver]](None)
|
2016-05-09 07:31:41 +02:00
|
|
|
@volatile private[this] var aeron: Aeron = _
|
2016-05-18 13:34:51 +02:00
|
|
|
@volatile private[this] var aeronErrorLogTask: Cancellable = _
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-07-04 15:59:44 +02:00
|
|
|
@volatile private[this] var inboundCompressions: Option[InboundCompressions] = None
|
2016-07-04 16:42:14 +02:00
|
|
|
|
2016-05-27 16:45:48 +02:00
|
|
|
override def localAddress: UniqueAddress = _localAddress
|
2016-05-09 07:31:41 +02:00
|
|
|
override def defaultAddress: Address = localAddress.address
|
2016-05-27 16:45:48 +02:00
|
|
|
override def addresses: Set[Address] = _addresses
|
2016-05-09 07:31:41 +02:00
|
|
|
override def localAddressForRemote(remote: Address): Address = defaultAddress
|
2016-05-13 15:34:37 +02:00
|
|
|
override val log: LoggingAdapter = Logging(system, getClass.getName)
|
2016-09-01 09:07:39 +03:00
|
|
|
val eventPublisher = new EventPublisher(system, log, settings.LifecycleEventsLogLevel)
|
2016-05-09 07:31:41 +02:00
|
|
|
|
|
|
|
|
private val codec: AkkaPduCodec = AkkaPduProtobufCodec
|
|
|
|
|
private val killSwitch: SharedKillSwitch = KillSwitches.shared("transportKillSwitch")
|
2016-09-06 11:50:10 +02:00
|
|
|
private[this] val streamCompletions = new AtomicReference(Map.empty[String, Future[Done]])
|
2016-05-17 17:34:57 +02:00
|
|
|
@volatile private[this] var _shutdown = false
|
2016-05-12 11:42:09 +02:00
|
|
|
|
2016-06-02 07:21:32 +02:00
|
|
|
private val testStages: CopyOnWriteArrayList[TestManagementApi] = new CopyOnWriteArrayList
|
|
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
private val inboundLanes = settings.Advanced.InboundLanes
|
2016-06-23 18:11:56 +02:00
|
|
|
|
2016-09-01 09:07:39 +03:00
|
|
|
private val remoteDispatcher = system.dispatchers.lookup(settings.Dispatcher)
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-08-23 20:38:39 +02:00
|
|
|
// TODO use WildcardIndex.isEmpty when merged from master
|
|
|
|
|
val largeMessageChannelEnabled =
|
2016-09-01 09:07:39 +03:00
|
|
|
!settings.LargeMessageDestinations.wildcardTree.isEmpty || !settings.LargeMessageDestinations.doubleWildcardTree.isEmpty
|
2016-05-20 12:40:56 +02:00
|
|
|
|
2016-08-26 14:44:33 +02:00
|
|
|
private val priorityMessageDestinations =
|
|
|
|
|
WildcardIndex[NotUsed]()
|
2016-08-24 19:52:07 +02:00
|
|
|
// These destinations are not defined in configuration because it should not
|
|
|
|
|
// be possible to abuse the control channel
|
2016-08-26 14:44:33 +02:00
|
|
|
.insert(Array("system", "remote-watcher"), NotUsed)
|
|
|
|
|
// these belongs to cluster and should come from there
|
|
|
|
|
.insert(Array("system", "cluster", "core", "daemon", "heartbeatSender"), NotUsed)
|
|
|
|
|
.insert(Array("system", "cluster", "heartbeatReceiver"), NotUsed)
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
private def inboundChannel = s"aeron:udp?endpoint=${localAddress.address.host.get}:${localAddress.address.port.get}"
|
|
|
|
|
private def outboundChannel(a: Address) = s"aeron:udp?endpoint=${a.host.get}:${a.port.get}"
|
2016-08-26 14:44:33 +02:00
|
|
|
|
2016-05-12 08:56:28 +02:00
|
|
|
private val controlStreamId = 1
|
2016-08-30 14:37:11 +02:00
|
|
|
private val ordinaryStreamId = 2
|
|
|
|
|
private val largeStreamId = 3
|
2016-08-26 14:44:33 +02:00
|
|
|
|
2016-09-01 09:07:39 +03:00
|
|
|
private val taskRunner = new TaskRunner(system, settings.Advanced.IdleCpuLevel)
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-09-01 09:07:39 +03:00
|
|
|
private val restartCounter = new RestartCounter(settings.Advanced.InboundMaxRestarts, settings.Advanced.InboundRestartTimeout)
|
2016-05-17 17:34:57 +02:00
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
private val envelopeBufferPool = new EnvelopeBufferPool(settings.Advanced.MaximumFrameSize, settings.Advanced.MaximumPooledBuffers)
|
|
|
|
|
private val largeEnvelopeBufferPool = new EnvelopeBufferPool(settings.Advanced.MaximumLargeFrameSize, settings.Advanced.MaximumPooledBuffers)
|
2016-06-06 08:26:15 +02:00
|
|
|
|
2016-06-29 17:09:33 +02:00
|
|
|
private val inboundEnvelopePool = ReusableInboundEnvelope.createObjectPool(capacity = 16)
|
|
|
|
|
// FIXME capacity of outboundEnvelopePool should probably be derived from the sendQueue capacity
|
|
|
|
|
// times a factor (for reasonable number of outbound streams)
|
|
|
|
|
private val outboundEnvelopePool = ReusableOutboundEnvelope.createObjectPool(capacity = 3072 * 2)
|
2016-05-20 12:40:56 +02:00
|
|
|
|
2016-06-29 12:36:17 +02:00
|
|
|
val (afrFileChannel, afrFlie, flightRecorder) = initializeFlightRecorder() match {
|
|
|
|
|
case None ⇒ (None, None, None)
|
|
|
|
|
case Some((c, f, r)) ⇒ (Some(c), Some(f), Some(r))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def createFlightRecorderEventSink(synchr: Boolean = false): EventSink = {
|
|
|
|
|
flightRecorder match {
|
|
|
|
|
case Some(f) ⇒
|
|
|
|
|
val eventSink = f.createEventSink()
|
|
|
|
|
if (synchr) new SynchronizedEventSink(eventSink)
|
|
|
|
|
else eventSink
|
|
|
|
|
case None ⇒
|
|
|
|
|
IgnoreEventSink
|
|
|
|
|
}
|
2016-06-23 18:11:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-29 12:36:17 +02:00
|
|
|
private val topLevelFREvents =
|
|
|
|
|
createFlightRecorderEventSink(synchr = true)
|
2016-06-06 13:36:05 +02:00
|
|
|
|
2016-05-25 12:28:44 +02:00
|
|
|
private val associationRegistry = new AssociationRegistry(
|
2016-08-26 14:44:33 +02:00
|
|
|
remoteAddress ⇒ new Association(
|
|
|
|
|
this,
|
|
|
|
|
materializer,
|
|
|
|
|
remoteAddress,
|
|
|
|
|
controlSubject,
|
2016-09-01 09:07:39 +03:00
|
|
|
settings.LargeMessageDestinations,
|
2016-08-26 14:44:33 +02:00
|
|
|
priorityMessageDestinations,
|
2016-06-29 17:09:33 +02:00
|
|
|
outboundEnvelopePool))
|
2016-05-25 12:28:44 +02:00
|
|
|
|
2016-09-07 10:41:36 +02:00
|
|
|
override def settings = provider.remoteSettings.Artery
|
2016-06-02 07:21:32 +02:00
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
override def start(): Unit = {
|
|
|
|
|
startMediaDriver()
|
|
|
|
|
startAeron()
|
2016-06-06 13:36:05 +02:00
|
|
|
topLevelFREvents.loFreq(Transport_AeronStarted, NoMetaData)
|
2016-05-18 13:34:51 +02:00
|
|
|
startAeronErrorLog()
|
2016-06-06 13:36:05 +02:00
|
|
|
topLevelFREvents.loFreq(Transport_AeronErrorLogStarted, NoMetaData)
|
2016-05-09 07:31:41 +02:00
|
|
|
taskRunner.start()
|
2016-06-06 13:36:05 +02:00
|
|
|
topLevelFREvents.loFreq(Transport_TaskRunnerStarted, NoMetaData)
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-05-17 14:17:21 +02:00
|
|
|
val port =
|
2016-09-01 09:07:39 +03:00
|
|
|
if (settings.Port == 0) ArteryTransport.autoSelectPort(settings.Hostname)
|
|
|
|
|
else settings.Port
|
2016-05-17 14:17:21 +02:00
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
// TODO: Configure materializer properly
|
|
|
|
|
// TODO: Have a supervisor actor
|
|
|
|
|
_localAddress = UniqueAddress(
|
2016-09-01 09:07:39 +03:00
|
|
|
Address(ArteryTransport.ProtocolName, system.name, settings.Hostname, port),
|
2016-05-25 12:28:44 +02:00
|
|
|
AddressUidExtension(system).longAddressUid)
|
2016-05-27 16:45:48 +02:00
|
|
|
_addresses = Set(_localAddress.address)
|
2016-05-29 19:41:09 +02:00
|
|
|
|
2016-06-06 13:36:05 +02:00
|
|
|
// TODO: This probably needs to be a global value instead of an event as events might rotate out of the log
|
|
|
|
|
topLevelFREvents.loFreq(Transport_UniqueAddressSet, _localAddress.toString().getBytes("US-ASCII"))
|
|
|
|
|
|
2016-09-01 09:07:39 +03:00
|
|
|
materializer = ActorMaterializer.systemMaterializer(settings.Advanced.MaterializerSettings, "remote", system)
|
2016-05-09 07:31:41 +02:00
|
|
|
|
|
|
|
|
messageDispatcher = new MessageDispatcher(system, provider)
|
2016-06-06 13:36:05 +02:00
|
|
|
topLevelFREvents.loFreq(Transport_MaterializerStarted, NoMetaData)
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-05-17 17:34:57 +02:00
|
|
|
runInboundStreams()
|
2016-06-06 13:36:05 +02:00
|
|
|
topLevelFREvents.loFreq(Transport_StartupFinished, NoMetaData)
|
2016-05-18 09:22:22 +02:00
|
|
|
|
|
|
|
|
log.info("Remoting started; listening on address: {}", defaultAddress)
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-08 18:38:54 +02:00
|
|
|
private lazy val stopMediaDriverShutdownHook = new Thread {
|
|
|
|
|
override def run(): Unit = stopMediaDriver()
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
private def startMediaDriver(): Unit = {
|
2016-09-01 09:07:39 +03:00
|
|
|
if (settings.Advanced.EmbeddedMediaDriver) {
|
2016-06-01 11:56:18 +02:00
|
|
|
val driverContext = new MediaDriver.Context
|
2016-09-01 09:07:39 +03:00
|
|
|
if (settings.Advanced.AeronDirectoryName.nonEmpty)
|
|
|
|
|
driverContext.aeronDirectoryName(settings.Advanced.AeronDirectoryName)
|
|
|
|
|
driverContext.clientLivenessTimeoutNs(settings.Advanced.ClientLivenessTimeout.toNanos)
|
|
|
|
|
driverContext.imageLivenessTimeoutNs(settings.Advanced.ImageLivenessTimeoutNs.toNanos)
|
|
|
|
|
driverContext.driverTimeoutMs(settings.Advanced.DriverTimeout.toMillis)
|
|
|
|
|
|
|
|
|
|
val idleCpuLevel = settings.Advanced.IdleCpuLevel
|
2016-06-15 15:12:33 +02:00
|
|
|
if (idleCpuLevel == 10) {
|
2016-06-10 07:41:36 +02:00
|
|
|
driverContext
|
|
|
|
|
.threadingMode(ThreadingMode.DEDICATED)
|
|
|
|
|
.conductorIdleStrategy(new BackoffIdleStrategy(1, 1, 1, 1))
|
2016-06-15 15:12:33 +02:00
|
|
|
.receiverIdleStrategy(TaskRunner.createIdleStrategy(idleCpuLevel))
|
|
|
|
|
.senderIdleStrategy(TaskRunner.createIdleStrategy(idleCpuLevel))
|
|
|
|
|
} else if (idleCpuLevel == 1) {
|
2016-06-10 07:41:36 +02:00
|
|
|
driverContext
|
|
|
|
|
.threadingMode(ThreadingMode.SHARED)
|
2016-06-15 15:12:33 +02:00
|
|
|
.sharedIdleStrategy(TaskRunner.createIdleStrategy(idleCpuLevel))
|
|
|
|
|
} else if (idleCpuLevel <= 7) {
|
2016-06-10 07:41:36 +02:00
|
|
|
driverContext
|
|
|
|
|
.threadingMode(ThreadingMode.SHARED_NETWORK)
|
2016-06-15 15:12:33 +02:00
|
|
|
.sharedNetworkIdleStrategy(TaskRunner.createIdleStrategy(idleCpuLevel))
|
|
|
|
|
} else {
|
|
|
|
|
driverContext
|
|
|
|
|
.threadingMode(ThreadingMode.DEDICATED)
|
|
|
|
|
.receiverIdleStrategy(TaskRunner.createIdleStrategy(idleCpuLevel))
|
|
|
|
|
.senderIdleStrategy(TaskRunner.createIdleStrategy(idleCpuLevel))
|
2016-06-10 07:41:36 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-01 11:56:18 +02:00
|
|
|
val driver = MediaDriver.launchEmbedded(driverContext)
|
|
|
|
|
log.debug("Started embedded media driver in directory [{}]", driver.aeronDirectoryName)
|
2016-06-06 13:36:05 +02:00
|
|
|
topLevelFREvents.loFreq(Transport_MediaDriverStarted, driver.aeronDirectoryName().getBytes("US-ASCII"))
|
2016-06-08 18:38:54 +02:00
|
|
|
Runtime.getRuntime.addShutdownHook(stopMediaDriverShutdownHook)
|
2016-09-02 16:40:38 +02:00
|
|
|
if (!mediaDriver.compareAndSet(None, Some(driver))) {
|
|
|
|
|
throw new IllegalStateException("media driver started more than once")
|
|
|
|
|
}
|
2016-06-01 11:56:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-02 16:40:38 +02:00
|
|
|
private def aeronDir: String = mediaDriver.get match {
|
2016-06-01 11:56:18 +02:00
|
|
|
case Some(driver) ⇒ driver.aeronDirectoryName
|
2016-09-01 09:07:39 +03:00
|
|
|
case None ⇒ settings.Advanced.AeronDirectoryName
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-08 18:38:54 +02:00
|
|
|
private def stopMediaDriver(): Unit = {
|
2016-09-02 16:40:38 +02:00
|
|
|
// make sure we only close the driver once or we will crash the JVM
|
|
|
|
|
val maybeDriver = mediaDriver.getAndSet(None)
|
|
|
|
|
maybeDriver.foreach { driver ⇒
|
2016-06-08 18:38:54 +02:00
|
|
|
// this is only for embedded media driver
|
|
|
|
|
driver.close()
|
2016-09-02 16:40:38 +02:00
|
|
|
|
2016-06-08 18:38:54 +02:00
|
|
|
try {
|
2016-09-01 09:07:39 +03:00
|
|
|
if (settings.Advanced.DeleteAeronDirectory) {
|
|
|
|
|
IoUtil.delete(new File(driver.aeronDirectoryName), false)
|
|
|
|
|
}
|
2016-06-08 18:38:54 +02:00
|
|
|
} catch {
|
|
|
|
|
case NonFatal(e) ⇒
|
|
|
|
|
log.warning(
|
|
|
|
|
"Couldn't delete Aeron embedded media driver files in [{}] due to [{}]",
|
|
|
|
|
driver.aeronDirectoryName, e.getMessage)
|
|
|
|
|
}
|
2016-09-02 16:40:38 +02:00
|
|
|
Try(Runtime.getRuntime.removeShutdownHook(stopMediaDriverShutdownHook))
|
2016-06-08 18:38:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 13:36:05 +02:00
|
|
|
// TODO: Add FR events
|
2016-05-09 07:31:41 +02:00
|
|
|
private def startAeron(): Unit = {
|
|
|
|
|
val ctx = new Aeron.Context
|
|
|
|
|
|
2016-09-06 14:32:42 +02:00
|
|
|
ctx.driverTimeoutMs(settings.Advanced.DriverTimeout.toMillis)
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
ctx.availableImageHandler(new AvailableImageHandler {
|
|
|
|
|
override def onAvailableImage(img: Image): Unit = {
|
|
|
|
|
if (log.isDebugEnabled)
|
|
|
|
|
log.debug(s"onAvailableImage from ${img.sourceIdentity} session ${img.sessionId}")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
ctx.unavailableImageHandler(new UnavailableImageHandler {
|
|
|
|
|
override def onUnavailableImage(img: Image): Unit = {
|
|
|
|
|
if (log.isDebugEnabled)
|
|
|
|
|
log.debug(s"onUnavailableImage from ${img.sourceIdentity} session ${img.sessionId}")
|
|
|
|
|
// FIXME we should call FragmentAssembler.freeSessionBuffer when image is unavailable
|
|
|
|
|
}
|
|
|
|
|
})
|
2016-09-06 14:32:42 +02:00
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
ctx.errorHandler(new ErrorHandler {
|
2016-09-06 14:32:42 +02:00
|
|
|
private val fatalErrorOccured = new AtomicBoolean
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
override def onError(cause: Throwable): Unit = {
|
|
|
|
|
cause match {
|
2016-09-06 14:32:42 +02:00
|
|
|
case e: ConductorServiceTimeoutException ⇒ handleFatalError(e)
|
|
|
|
|
case e: DriverTimeoutException ⇒ handleFatalError(e)
|
|
|
|
|
case _: AeronTerminated ⇒ // already handled, via handleFatalError
|
2016-05-09 07:31:41 +02:00
|
|
|
case _ ⇒
|
|
|
|
|
log.error(cause, s"Aeron error, ${cause.getMessage}")
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-09-06 14:32:42 +02:00
|
|
|
|
|
|
|
|
private def handleFatalError(cause: Throwable): Unit = {
|
|
|
|
|
if (fatalErrorOccured.compareAndSet(false, true)) {
|
|
|
|
|
if (!isShutdown) {
|
|
|
|
|
log.error(cause, "Fatal Aeron error {}. Have to terminate ActorSystem because it lost contact with the " +
|
|
|
|
|
"{} Aeron media driver. Possible configuration properties to mitigate the problem are " +
|
|
|
|
|
"'client-liveness-timeout' or 'driver-timeout'. {}",
|
|
|
|
|
Logging.simpleName(cause),
|
|
|
|
|
if (settings.Advanced.EmbeddedMediaDriver) "embedded" else "external",
|
|
|
|
|
cause.getMessage)
|
|
|
|
|
taskRunner.stop()
|
|
|
|
|
aeronErrorLogTask.cancel()
|
|
|
|
|
system.terminate()
|
|
|
|
|
throw new AeronTerminated(cause)
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
throw new AeronTerminated(cause)
|
|
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
})
|
|
|
|
|
|
2016-06-01 11:56:18 +02:00
|
|
|
ctx.aeronDirectoryName(aeronDir)
|
2016-05-09 07:31:41 +02:00
|
|
|
aeron = Aeron.connect(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 13:36:05 +02:00
|
|
|
// TODO Add FR Events
|
2016-05-18 13:34:51 +02:00
|
|
|
private def startAeronErrorLog(): Unit = {
|
2016-06-01 11:56:18 +02:00
|
|
|
val errorLog = new AeronErrorLog(new File(aeronDir, CncFileDescriptor.CNC_FILE))
|
2016-05-18 13:34:51 +02:00
|
|
|
val lastTimestamp = new AtomicLong(0L)
|
|
|
|
|
import system.dispatcher // FIXME perhaps use another dispatcher for this
|
|
|
|
|
aeronErrorLogTask = system.scheduler.schedule(3.seconds, 5.seconds) {
|
|
|
|
|
if (!isShutdown) {
|
|
|
|
|
val newLastTimestamp = errorLog.logErrors(log, lastTimestamp.get)
|
|
|
|
|
lastTimestamp.set(newLastTimestamp + 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-17 17:34:57 +02:00
|
|
|
private def runInboundStreams(): Unit = {
|
2016-07-04 16:42:14 +02:00
|
|
|
val noCompressions = NoInboundCompressions // TODO possibly enable on other channels too https://github.com/akka/akka/pull/20546/files#r68074082
|
2016-07-01 11:54:57 +02:00
|
|
|
val compressions = createInboundCompressions(this)
|
2016-07-04 15:59:44 +02:00
|
|
|
inboundCompressions = Some(compressions)
|
2016-06-23 11:58:54 +02:00
|
|
|
|
2016-07-04 16:42:14 +02:00
|
|
|
runInboundControlStream(noCompressions) // TODO should understand compressions too
|
2016-07-01 11:54:57 +02:00
|
|
|
runInboundOrdinaryMessagesStream(compressions)
|
2016-08-23 20:38:39 +02:00
|
|
|
if (largeMessageChannelEnabled) {
|
2016-05-20 12:40:56 +02:00
|
|
|
runInboundLargeMessagesStream()
|
|
|
|
|
}
|
2016-05-17 17:34:57 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-01 11:54:57 +02:00
|
|
|
private def runInboundControlStream(compression: InboundCompressions): Unit = {
|
2016-08-30 14:37:11 +02:00
|
|
|
val (testMgmt, ctrl, completed) =
|
|
|
|
|
aeronSource(controlStreamId, envelopeBufferPool)
|
|
|
|
|
.via(inboundFlow(compression))
|
|
|
|
|
.toMat(inboundControlSink)(Keep.right)
|
|
|
|
|
.run()(materializer)
|
|
|
|
|
|
|
|
|
|
if (settings.Advanced.TestMode)
|
|
|
|
|
testStages.add(testMgmt)
|
2016-06-02 07:21:32 +02:00
|
|
|
|
|
|
|
|
controlSubject = ctrl
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-05-13 15:34:37 +02:00
|
|
|
controlSubject.attach(new ControlMessageObserver {
|
|
|
|
|
override def notify(inboundEnvelope: InboundEnvelope): Unit = {
|
2016-07-04 15:59:44 +02:00
|
|
|
|
2016-05-17 17:34:57 +02:00
|
|
|
inboundEnvelope.message match {
|
2016-06-23 11:58:54 +02:00
|
|
|
case m: CompressionMessage ⇒
|
2016-07-04 15:59:44 +02:00
|
|
|
import CompressionProtocol._
|
2016-06-23 11:58:54 +02:00
|
|
|
m match {
|
2016-07-04 15:59:44 +02:00
|
|
|
case ActorRefCompressionAdvertisement(from, table) ⇒
|
2016-07-01 11:54:57 +02:00
|
|
|
log.debug("Incoming ActorRef compression advertisement from [{}], table: [{}]", from, table)
|
2016-07-04 15:59:44 +02:00
|
|
|
val a = association(from.address)
|
2016-08-24 19:52:07 +02:00
|
|
|
// make sure uid is same for active association
|
|
|
|
|
if (a.associationState.uniqueRemoteAddressValue().contains(from)) {
|
|
|
|
|
import system.dispatcher
|
|
|
|
|
a.changeActorRefCompression(table).foreach { _ ⇒
|
|
|
|
|
a.sendControl(ActorRefCompressionAdvertisementAck(localAddress, table.version))
|
|
|
|
|
system.eventStream.publish(Events.ReceivedActorRefCompressionTable(from, table))
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-04 15:59:44 +02:00
|
|
|
case ActorRefCompressionAdvertisementAck(from, tableVersion) ⇒
|
|
|
|
|
inboundCompressions.foreach(_.confirmActorRefCompressionAdvertisement(from.uid, tableVersion))
|
|
|
|
|
case ClassManifestCompressionAdvertisement(from, table) ⇒
|
2016-07-01 11:54:57 +02:00
|
|
|
log.debug("Incoming Class Manifest compression advertisement from [{}], table: [{}]", from, table)
|
2016-07-04 15:59:44 +02:00
|
|
|
val a = association(from.address)
|
2016-08-24 19:52:07 +02:00
|
|
|
// make sure uid is same for active association
|
|
|
|
|
if (a.associationState.uniqueRemoteAddressValue().contains(from)) {
|
|
|
|
|
import system.dispatcher
|
|
|
|
|
a.changeClassManifestCompression(table).foreach { _ ⇒
|
|
|
|
|
a.sendControl(ClassManifestCompressionAdvertisementAck(localAddress, table.version))
|
|
|
|
|
system.eventStream.publish(Events.ReceivedClassManifestCompressionTable(from, table))
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-04 15:59:44 +02:00
|
|
|
case ClassManifestCompressionAdvertisementAck(from, tableVersion) ⇒
|
|
|
|
|
inboundCompressions.foreach(_.confirmActorRefCompressionAdvertisement(from.uid, tableVersion))
|
2016-06-23 11:58:54 +02:00
|
|
|
}
|
2016-06-23 18:11:56 +02:00
|
|
|
|
|
|
|
|
case Quarantined(from, to) if to == localAddress ⇒
|
|
|
|
|
val lifecycleEvent = ThisActorSystemQuarantinedEvent(localAddress.address, from.address)
|
|
|
|
|
publishLifecycleEvent(lifecycleEvent)
|
|
|
|
|
// quarantine the other system from here
|
|
|
|
|
association(from.address).quarantine(lifecycleEvent.toString, Some(from.uid))
|
|
|
|
|
|
|
|
|
|
case _: ActorSystemTerminating ⇒
|
|
|
|
|
inboundEnvelope.sender match {
|
|
|
|
|
case OptionVal.Some(snd) ⇒ snd.tell(ActorSystemTerminatingAck(localAddress), ActorRef.noSender)
|
|
|
|
|
case OptionVal.None ⇒ log.error("Expected sender for ActorSystemTerminating message")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case _ ⇒ // not interesting
|
2016-05-17 17:34:57 +02:00
|
|
|
}
|
2016-06-23 18:11:56 +02:00
|
|
|
}
|
2016-05-17 17:34:57 +02:00
|
|
|
})
|
|
|
|
|
|
2016-06-23 11:58:54 +02:00
|
|
|
attachStreamRestart("Inbound control stream", completed, () ⇒ runInboundControlStream(compression))
|
2016-05-17 17:34:57 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-01 11:54:57 +02:00
|
|
|
private def runInboundOrdinaryMessagesStream(compression: InboundCompressions): Unit = {
|
2016-06-02 07:21:32 +02:00
|
|
|
val completed =
|
2016-08-30 14:37:11 +02:00
|
|
|
if (inboundLanes == 1) {
|
|
|
|
|
val (testMgmt, completed) = aeronSource(ordinaryStreamId, envelopeBufferPool)
|
2016-06-23 11:58:54 +02:00
|
|
|
.via(inboundFlow(compression))
|
2016-08-30 14:37:11 +02:00
|
|
|
.toMat(inboundSink(envelopeBufferPool))(Keep.right)
|
2016-06-02 07:21:32 +02:00
|
|
|
.run()(materializer)
|
2016-08-30 14:37:11 +02:00
|
|
|
|
|
|
|
|
if (settings.Advanced.TestMode)
|
|
|
|
|
testStages.add(testMgmt)
|
|
|
|
|
|
|
|
|
|
completed
|
|
|
|
|
|
2016-06-02 07:21:32 +02:00
|
|
|
} else {
|
2016-09-06 16:57:30 +02:00
|
|
|
val hubKillSwitch = KillSwitches.shared("hubKillSwitch")
|
2016-08-30 14:37:11 +02:00
|
|
|
val source = aeronSource(ordinaryStreamId, envelopeBufferPool)
|
2016-09-06 16:57:30 +02:00
|
|
|
.via(hubKillSwitch.flow)
|
2016-06-23 11:58:54 +02:00
|
|
|
.via(inboundFlow(compression))
|
2016-08-30 14:37:11 +02:00
|
|
|
.map(env ⇒ (env.recipient, env))
|
|
|
|
|
|
|
|
|
|
val broadcastHub = source.runWith(BroadcastHub.sink(bufferSize = settings.Advanced.InboundBroadcastHubBufferSize))(materializer)
|
|
|
|
|
|
|
|
|
|
val lane = inboundSink(envelopeBufferPool)
|
|
|
|
|
|
|
|
|
|
// select lane based on destination, to preserve message order
|
|
|
|
|
val partitionFun: OptionVal[ActorRef] ⇒ Int = {
|
|
|
|
|
_ match {
|
|
|
|
|
case OptionVal.Some(r) ⇒ math.abs(r.path.uid) % inboundLanes
|
|
|
|
|
case OptionVal.None ⇒ 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val values: Vector[(TestManagementApi, Future[Done])] =
|
|
|
|
|
(0 until inboundLanes).map { i ⇒
|
|
|
|
|
broadcastHub.runWith(
|
|
|
|
|
// TODO replace filter with "PartitionHub" when that is implemented
|
|
|
|
|
// must use a tuple here because envelope is pooled and must only be touched in the selected lane
|
|
|
|
|
Flow[(OptionVal[ActorRef], InboundEnvelope)].collect {
|
|
|
|
|
case (recipient, env) if partitionFun(recipient) == i ⇒ env
|
|
|
|
|
}
|
|
|
|
|
.toMat(lane)(Keep.right))(materializer)
|
|
|
|
|
}(collection.breakOut)
|
|
|
|
|
|
|
|
|
|
val (testMgmtValues, completedValues) = values.unzip
|
|
|
|
|
|
|
|
|
|
if (settings.Advanced.TestMode)
|
|
|
|
|
testMgmtValues.foreach(testStages.add)
|
|
|
|
|
|
|
|
|
|
import system.dispatcher
|
|
|
|
|
val completed = Future.sequence(completedValues).map(_ ⇒ Done)
|
|
|
|
|
|
2016-09-06 16:57:30 +02:00
|
|
|
// tear down the upstream hub part if downstream lane fails
|
|
|
|
|
// lanes are not completed with success by themselves so we don't have to care about onSuccess
|
|
|
|
|
completed.onFailure {
|
|
|
|
|
case reason: Throwable ⇒ hubKillSwitch.abort(reason)
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
completed
|
2016-06-02 07:21:32 +02:00
|
|
|
}
|
2016-05-17 17:34:57 +02:00
|
|
|
|
2016-06-23 11:58:54 +02:00
|
|
|
attachStreamRestart("Inbound message stream", completed, () ⇒ runInboundOrdinaryMessagesStream(compression))
|
2016-05-17 17:34:57 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-20 12:40:56 +02:00
|
|
|
private def runInboundLargeMessagesStream(): Unit = {
|
2016-07-04 16:42:14 +02:00
|
|
|
val disableCompression = NoInboundCompressions // no compression on large message stream for now
|
2016-06-23 11:58:54 +02:00
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
val (testMgmt, completed) = aeronSource(largeStreamId, largeEnvelopeBufferPool)
|
|
|
|
|
.via(inboundLargeFlow(disableCompression))
|
|
|
|
|
.toMat(inboundSink(largeEnvelopeBufferPool))(Keep.right)
|
|
|
|
|
.run()(materializer)
|
|
|
|
|
|
|
|
|
|
if (settings.Advanced.TestMode)
|
|
|
|
|
testStages.add(testMgmt)
|
2016-06-02 07:21:32 +02:00
|
|
|
|
2016-05-26 10:42:08 +02:00
|
|
|
attachStreamRestart("Inbound large message stream", completed, () ⇒ runInboundLargeMessagesStream())
|
2016-05-20 12:40:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-17 17:34:57 +02:00
|
|
|
private def attachStreamRestart(streamName: String, streamCompleted: Future[Done], restart: () ⇒ Unit): Unit = {
|
|
|
|
|
implicit val ec = materializer.executionContext
|
2016-09-06 11:50:10 +02:00
|
|
|
updateStreamCompletion(streamName, streamCompleted.recover { case _ ⇒ Done })
|
2016-05-17 17:34:57 +02:00
|
|
|
streamCompleted.onFailure {
|
2016-09-05 15:08:30 +02:00
|
|
|
case ShutdownSignal ⇒ // shutdown as expected
|
|
|
|
|
case _: AeronTerminated ⇒ // shutdown already in progress
|
2016-09-06 11:50:10 +02:00
|
|
|
case cause if isShutdown ⇒
|
|
|
|
|
// don't restart after shutdown, but log some details so we notice
|
|
|
|
|
log.error(cause, s"{} failed after shutdown. {}", streamName, cause.getMessage)
|
2016-05-17 17:34:57 +02:00
|
|
|
case _: AbruptTerminationException ⇒ // ActorSystem shutdown
|
|
|
|
|
case cause ⇒
|
2016-05-19 08:24:27 +02:00
|
|
|
if (restartCounter.restart()) {
|
|
|
|
|
log.error(cause, "{} failed. Restarting it. {}", streamName, cause.getMessage)
|
|
|
|
|
restart()
|
|
|
|
|
} else {
|
|
|
|
|
log.error(cause, "{} failed and restarted {} times within {} seconds. Terminating system. {}",
|
2016-09-01 09:07:39 +03:00
|
|
|
streamName, settings.Advanced.InboundMaxRestarts, settings.Advanced.InboundRestartTimeout.toSeconds, cause.getMessage)
|
2016-05-19 08:24:27 +02:00
|
|
|
system.terminate()
|
|
|
|
|
}
|
2016-05-17 17:34:57 +02:00
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def shutdown(): Future[Done] = {
|
2016-05-17 17:34:57 +02:00
|
|
|
_shutdown = true
|
2016-06-23 18:11:56 +02:00
|
|
|
val allAssociations = associationRegistry.allAssociations
|
|
|
|
|
val flushing: Future[Done] =
|
|
|
|
|
if (allAssociations.isEmpty) Future.successful(Done)
|
|
|
|
|
else {
|
|
|
|
|
val flushingPromise = Promise[Done]()
|
2016-09-01 09:07:39 +03:00
|
|
|
system.systemActorOf(FlushOnShutdown.props(flushingPromise, settings.Advanced.ShutdownFlushTimeout,
|
|
|
|
|
this, allAssociations).withDispatcher(settings.Dispatcher), "remoteFlushOnShutdown")
|
2016-06-23 18:11:56 +02:00
|
|
|
flushingPromise.future
|
|
|
|
|
}
|
|
|
|
|
implicit val ec = remoteDispatcher
|
|
|
|
|
|
2016-09-06 11:50:10 +02:00
|
|
|
for {
|
|
|
|
|
_ ← flushing.recover { case _ ⇒ Done }
|
2016-09-07 08:27:33 +02:00
|
|
|
_ = killSwitch.abort(ShutdownSignal)
|
2016-09-06 11:50:10 +02:00
|
|
|
_ ← streamsCompleted
|
|
|
|
|
} yield {
|
2016-06-23 18:11:56 +02:00
|
|
|
topLevelFREvents.loFreq(Transport_KillSwitchPulled, NoMetaData)
|
2016-09-06 14:32:42 +02:00
|
|
|
taskRunner.stop()
|
|
|
|
|
topLevelFREvents.loFreq(Transport_Stopped, NoMetaData)
|
|
|
|
|
|
2016-06-23 18:11:56 +02:00
|
|
|
if (aeronErrorLogTask != null) {
|
|
|
|
|
aeronErrorLogTask.cancel()
|
|
|
|
|
topLevelFREvents.loFreq(Transport_AeronErrorLogTaskStopped, NoMetaData)
|
|
|
|
|
}
|
|
|
|
|
if (aeron != null) aeron.close()
|
2016-09-02 16:40:38 +02:00
|
|
|
if (mediaDriver.get.isDefined) {
|
2016-06-23 18:11:56 +02:00
|
|
|
stopMediaDriver()
|
|
|
|
|
topLevelFREvents.loFreq(Transport_MediaFileDeleted, NoMetaData)
|
|
|
|
|
}
|
|
|
|
|
topLevelFREvents.loFreq(Transport_FlightRecorderClose, NoMetaData)
|
|
|
|
|
|
2016-06-29 12:36:17 +02:00
|
|
|
flightRecorder.foreach(_.close())
|
|
|
|
|
afrFileChannel.foreach(_.force(true))
|
|
|
|
|
afrFileChannel.foreach(_.close())
|
2016-06-23 18:11:56 +02:00
|
|
|
// TODO: Be smarter about this in tests and make it always-on-for prod
|
2016-06-29 12:36:17 +02:00
|
|
|
afrFlie.foreach(_.delete())
|
2016-06-23 18:11:56 +02:00
|
|
|
Done
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 11:50:10 +02:00
|
|
|
// set the future that completes when the current stream for a given name completes
|
|
|
|
|
@tailrec
|
|
|
|
|
private def updateStreamCompletion(streamName: String, streamCompleted: Future[Done]): Unit = {
|
|
|
|
|
val prev = streamCompletions.get()
|
|
|
|
|
if (!streamCompletions.compareAndSet(prev, prev + (streamName → streamCompleted))) {
|
|
|
|
|
updateStreamCompletion(streamName, streamCompleted)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Exposed for orderly shutdown purposes, can not be trusted except for during shutdown as streams may restart.
|
|
|
|
|
* Will complete successfully even if one of the stream completion futures failed
|
|
|
|
|
*/
|
|
|
|
|
private def streamsCompleted: Future[Done] = {
|
|
|
|
|
implicit val ec = remoteDispatcher
|
|
|
|
|
for {
|
|
|
|
|
_ ← Future.traverse(associationRegistry.allAssociations)(_.streamsCompleted)
|
|
|
|
|
_ ← Future.sequence(streamCompletions.get().valuesIterator)
|
|
|
|
|
} yield Done
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-23 11:58:54 +02:00
|
|
|
private[remote] def isShutdown: Boolean = _shutdown
|
2016-05-17 17:34:57 +02:00
|
|
|
|
2016-06-02 07:21:32 +02:00
|
|
|
override def managementCommand(cmd: Any): Future[Boolean] = {
|
|
|
|
|
if (testStages.isEmpty)
|
|
|
|
|
Future.successful(false)
|
|
|
|
|
else {
|
|
|
|
|
import system.dispatcher
|
2016-08-24 19:52:07 +02:00
|
|
|
import scala.collection.JavaConverters._
|
2016-06-02 07:21:32 +02:00
|
|
|
val allTestStages = testStages.asScala.toVector ++ associationRegistry.allAssociations.flatMap(_.testStages)
|
|
|
|
|
Future.sequence(allTestStages.map(_.send(cmd))).map(_ ⇒ true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
// InboundContext
|
2016-05-12 08:56:28 +02:00
|
|
|
override def sendControl(to: Address, message: ControlMessage) =
|
2016-05-29 22:15:48 +02:00
|
|
|
association(to).sendControl(message)
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-06-09 09:16:44 +02:00
|
|
|
override def send(message: Any, sender: OptionVal[ActorRef], recipient: RemoteActorRef): Unit = {
|
2016-05-09 07:31:41 +02:00
|
|
|
val cached = recipient.cachedAssociation
|
|
|
|
|
|
|
|
|
|
val a =
|
|
|
|
|
if (cached ne null) cached
|
2016-05-27 16:45:48 +02:00
|
|
|
else {
|
|
|
|
|
val a2 = association(recipient.path.address)
|
|
|
|
|
recipient.cachedAssociation = a2
|
|
|
|
|
a2
|
|
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-06-29 17:09:33 +02:00
|
|
|
a.send(message, sender, OptionVal.Some(recipient))
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-23 11:58:54 +02:00
|
|
|
override def association(remoteAddress: Address): Association = {
|
|
|
|
|
require(remoteAddress != localAddress.address, "Attemted association with self address!")
|
2016-05-25 12:28:44 +02:00
|
|
|
associationRegistry.association(remoteAddress)
|
2016-06-23 11:58:54 +02:00
|
|
|
}
|
2016-05-25 12:28:44 +02:00
|
|
|
|
2016-06-05 15:40:06 +02:00
|
|
|
override def association(uid: Long): OptionVal[Association] =
|
2016-05-25 12:28:44 +02:00
|
|
|
associationRegistry.association(uid)
|
|
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
override def completeHandshake(peer: UniqueAddress): Future[Done] = {
|
2016-05-25 12:28:44 +02:00
|
|
|
val a = associationRegistry.setUID(peer)
|
|
|
|
|
a.completeHandshake(peer)
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-13 15:34:37 +02:00
|
|
|
private def publishLifecycleEvent(event: RemotingLifecycleEvent): Unit =
|
|
|
|
|
eventPublisher.notifyListeners(event)
|
|
|
|
|
|
2016-05-25 12:28:44 +02:00
|
|
|
override def quarantine(remoteAddress: Address, uid: Option[Int]): Unit = {
|
|
|
|
|
// FIXME change the method signature (old remoting) to include reason and use Long uid?
|
|
|
|
|
association(remoteAddress).quarantine(reason = "", uid.map(_.toLong))
|
|
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
def outboundLarge(outboundContext: OutboundContext): Sink[OutboundEnvelope, Future[Done]] =
|
2016-08-30 14:37:11 +02:00
|
|
|
createOutboundSink(largeStreamId, outboundContext, largeEnvelopeBufferPool)
|
2016-08-24 19:52:07 +02:00
|
|
|
.mapMaterializedValue { case (_, d) ⇒ d }
|
2016-08-26 14:44:33 +02:00
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
def outbound(outboundContext: OutboundContext): Sink[OutboundEnvelope, (ChangeOutboundCompression, Future[Done])] =
|
|
|
|
|
createOutboundSink(ordinaryStreamId, outboundContext, envelopeBufferPool)
|
|
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
private def createOutboundSink(streamId: Int, outboundContext: OutboundContext,
|
2016-09-01 09:07:39 +03:00
|
|
|
bufferPool: EnvelopeBufferPool): Sink[OutboundEnvelope, (ChangeOutboundCompression, Future[Done])] = {
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
outboundLane(outboundContext, bufferPool)
|
|
|
|
|
.toMat(aeronSink(outboundContext, streamId))(Keep.both)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def aeronSink(outboundContext: OutboundContext): Sink[EnvelopeBuffer, Future[Done]] =
|
|
|
|
|
aeronSink(outboundContext, ordinaryStreamId)
|
|
|
|
|
|
|
|
|
|
private def aeronSink(outboundContext: OutboundContext, streamId: Int): Sink[EnvelopeBuffer, Future[Done]] = {
|
|
|
|
|
Sink.fromGraph(new AeronSink(outboundChannel(outboundContext.remoteAddress), streamId, aeron, taskRunner,
|
|
|
|
|
envelopeBufferPool, settings.Advanced.GiveUpSendAfter, createFlightRecorderEventSink()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def outboundLane(outboundContext: OutboundContext): Flow[OutboundEnvelope, EnvelopeBuffer, ChangeOutboundCompression] =
|
|
|
|
|
outboundLane(outboundContext, envelopeBufferPool)
|
|
|
|
|
|
|
|
|
|
private def outboundLane(
|
|
|
|
|
outboundContext: OutboundContext,
|
|
|
|
|
bufferPool: EnvelopeBufferPool): Flow[OutboundEnvelope, EnvelopeBuffer, ChangeOutboundCompression] = {
|
|
|
|
|
|
2016-06-29 17:09:33 +02:00
|
|
|
Flow.fromGraph(killSwitch.flow[OutboundEnvelope])
|
2016-08-30 14:37:11 +02:00
|
|
|
.via(new OutboundHandshake(system, outboundContext, outboundEnvelopePool, settings.Advanced.HandshakeTimeout,
|
2016-09-01 09:07:39 +03:00
|
|
|
settings.Advanced.HandshakeRetryInterval, settings.Advanced.InjectHandshakeInterval))
|
2016-08-24 19:52:07 +02:00
|
|
|
.viaMat(createEncoder(bufferPool))(Keep.right)
|
2016-05-20 12:40:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
def outboundControl(outboundContext: OutboundContext): Sink[OutboundEnvelope, (TestManagementApi, OutboundControlIngress, Future[Done])] = {
|
|
|
|
|
|
2016-06-29 17:09:33 +02:00
|
|
|
Flow.fromGraph(killSwitch.flow[OutboundEnvelope])
|
2016-08-30 14:37:11 +02:00
|
|
|
.via(new OutboundHandshake(system, outboundContext, outboundEnvelopePool, settings.Advanced.HandshakeTimeout,
|
2016-09-01 09:07:39 +03:00
|
|
|
settings.Advanced.HandshakeRetryInterval, settings.Advanced.InjectHandshakeInterval))
|
|
|
|
|
.via(new SystemMessageDelivery(outboundContext, system.deadLetters, settings.Advanced.SystemMessageResendInterval,
|
|
|
|
|
settings.Advanced.SysMsgBufferSize))
|
2016-08-30 14:37:11 +02:00
|
|
|
// note that System messages must not be dropped before the SystemMessageDelivery stage
|
|
|
|
|
.viaMat(outboundTestFlow(outboundContext))(Keep.right)
|
|
|
|
|
.viaMat(new OutboundControlJunction(outboundContext, outboundEnvelopePool))(Keep.both)
|
|
|
|
|
.via(createEncoder(envelopeBufferPool))
|
2016-05-19 08:24:27 +02:00
|
|
|
.toMat(new AeronSink(outboundChannel(outboundContext.remoteAddress), controlStreamId, aeron, taskRunner,
|
2016-08-30 14:37:11 +02:00
|
|
|
envelopeBufferPool, Duration.Inf, createFlightRecorderEventSink()))(Keep.both)
|
|
|
|
|
.mapMaterializedValue {
|
|
|
|
|
case ((a, b), c) ⇒ (a, b, c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO we can also add scrubbing stage that would collapse sys msg acks/nacks and remove duplicate Quarantine messages
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-01 11:54:57 +02:00
|
|
|
private def createInboundCompressions(inboundContext: InboundContext): InboundCompressions =
|
2016-09-02 08:52:09 +01:00
|
|
|
new InboundCompressionsImpl(system, inboundContext, settings.Advanced.Compression)
|
2016-06-23 11:58:54 +02:00
|
|
|
|
2016-08-24 19:52:07 +02:00
|
|
|
def createEncoder(pool: EnvelopeBufferPool): Flow[OutboundEnvelope, EnvelopeBuffer, ChangeOutboundCompression] =
|
|
|
|
|
Flow.fromGraph(new Encoder(localAddress, system, outboundEnvelopePool, pool))
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-06-02 07:21:32 +02:00
|
|
|
def aeronSource(streamId: Int, pool: EnvelopeBufferPool): Source[EnvelopeBuffer, NotUsed] =
|
|
|
|
|
Source.fromGraph(new AeronSource(inboundChannel, streamId, aeron, taskRunner, pool,
|
2016-06-23 18:11:56 +02:00
|
|
|
createFlightRecorderEventSink()))
|
2016-06-02 07:21:32 +02:00
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
val messageDispatcherSink: Sink[InboundEnvelope, Future[Done]] = Sink.foreach[InboundEnvelope] { m ⇒
|
2016-06-09 09:16:44 +02:00
|
|
|
messageDispatcher.dispatch(m.recipient.get, m.recipientAddress, m.message, m.sender)
|
2016-06-29 17:09:33 +02:00
|
|
|
m match {
|
|
|
|
|
case r: ReusableInboundEnvelope ⇒ inboundEnvelopePool.release(r)
|
|
|
|
|
case _ ⇒
|
|
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-01 11:54:57 +02:00
|
|
|
def createDecoder(compression: InboundCompressions, bufferPool: EnvelopeBufferPool): Flow[EnvelopeBuffer, InboundEnvelope, NotUsed] = {
|
2016-09-02 18:09:43 +02:00
|
|
|
Flow.fromGraph(new Decoder(this, system, localAddress, compression, bufferPool,
|
2016-06-06 08:26:15 +02:00
|
|
|
inboundEnvelopePool))
|
2016-05-26 10:42:08 +02:00
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
def createDeserializer(bufferPool: EnvelopeBufferPool): Flow[InboundEnvelope, InboundEnvelope, NotUsed] =
|
|
|
|
|
Flow.fromGraph(new Deserializer(this, system, bufferPool))
|
2016-05-26 10:42:08 +02:00
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
def inboundSink(bufferPool: EnvelopeBufferPool): Sink[InboundEnvelope, (TestManagementApi, Future[Done])] =
|
2016-05-26 10:42:08 +02:00
|
|
|
Flow[InboundEnvelope]
|
2016-08-30 14:37:11 +02:00
|
|
|
.via(createDeserializer(bufferPool))
|
|
|
|
|
.viaMat(new InboundTestStage(this, settings.Advanced.TestMode))(Keep.right)
|
2016-05-26 10:42:08 +02:00
|
|
|
.via(new InboundHandshake(this, inControlStream = false))
|
|
|
|
|
.via(new InboundQuarantineCheck(this))
|
2016-08-30 14:37:11 +02:00
|
|
|
.toMat(messageDispatcherSink)(Keep.both)
|
2016-05-26 10:42:08 +02:00
|
|
|
|
2016-07-01 11:54:57 +02:00
|
|
|
def inboundFlow(compression: InboundCompressions): Flow[EnvelopeBuffer, InboundEnvelope, NotUsed] = {
|
2016-06-02 07:21:32 +02:00
|
|
|
Flow[EnvelopeBuffer]
|
|
|
|
|
.via(killSwitch.flow)
|
2016-08-30 14:37:11 +02:00
|
|
|
.via(createDecoder(compression, envelopeBufferPool))
|
2016-05-26 10:42:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-01 11:54:57 +02:00
|
|
|
def inboundLargeFlow(compression: InboundCompressions): Flow[EnvelopeBuffer, InboundEnvelope, NotUsed] = {
|
2016-06-02 07:21:32 +02:00
|
|
|
Flow[EnvelopeBuffer]
|
|
|
|
|
.via(killSwitch.flow)
|
2016-08-30 14:37:11 +02:00
|
|
|
.via(createDecoder(compression, largeEnvelopeBufferPool))
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
def inboundControlSink: Sink[InboundEnvelope, (TestManagementApi, ControlMessageSubject, Future[Done])] = {
|
2016-06-02 07:21:32 +02:00
|
|
|
Flow[InboundEnvelope]
|
2016-08-30 14:37:11 +02:00
|
|
|
.via(createDeserializer(envelopeBufferPool))
|
|
|
|
|
.viaMat(new InboundTestStage(this, settings.Advanced.TestMode))(Keep.right)
|
2016-06-02 07:21:32 +02:00
|
|
|
.via(new InboundHandshake(this, inControlStream = true))
|
|
|
|
|
.via(new InboundQuarantineCheck(this))
|
2016-08-30 14:37:11 +02:00
|
|
|
.viaMat(new InboundControlJunction)(Keep.both)
|
2016-06-02 07:21:32 +02:00
|
|
|
.via(new SystemMessageAcker(this))
|
|
|
|
|
.toMat(messageDispatcherSink)(Keep.both)
|
2016-08-30 14:37:11 +02:00
|
|
|
.mapMaterializedValue {
|
|
|
|
|
case ((a, b), c) ⇒ (a, b, c)
|
|
|
|
|
}
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-29 12:36:17 +02:00
|
|
|
private def initializeFlightRecorder(): Option[(FileChannel, File, FlightRecorder)] = {
|
2016-09-01 09:07:39 +03:00
|
|
|
if (settings.Advanced.FlightRecorderEnabled) {
|
2016-06-29 12:36:17 +02:00
|
|
|
// TODO: Figure out where to put it, currently using temporary files
|
|
|
|
|
val afrFile = File.createTempFile("artery", ".afr")
|
|
|
|
|
afrFile.deleteOnExit()
|
2016-06-06 13:36:05 +02:00
|
|
|
|
2016-06-29 12:36:17 +02:00
|
|
|
val fileChannel = FlightRecorder.prepareFileForFlightRecorder(afrFile)
|
|
|
|
|
Some((fileChannel, afrFile, new FlightRecorder(fileChannel)))
|
|
|
|
|
} else
|
|
|
|
|
None
|
2016-06-06 13:36:05 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-30 14:37:11 +02:00
|
|
|
def outboundTestFlow(outboundContext: OutboundContext): Flow[OutboundEnvelope, OutboundEnvelope, TestManagementApi] =
|
|
|
|
|
Flow.fromGraph(new OutboundTestStage(outboundContext, settings.Advanced.TestMode))
|
2016-06-02 07:21:32 +02:00
|
|
|
|
2016-07-04 16:42:14 +02:00
|
|
|
/** INTERNAL API: for testing only. */
|
|
|
|
|
private[remote] def triggerCompressionAdvertisements(actorRef: Boolean, manifest: Boolean) = {
|
2016-07-04 15:59:44 +02:00
|
|
|
inboundCompressions.foreach {
|
2016-07-04 16:42:14 +02:00
|
|
|
case c: InboundCompressionsImpl if actorRef || manifest ⇒
|
|
|
|
|
log.info("Triggering compression table advertisement for {}", c)
|
|
|
|
|
if (actorRef) c.runNextActorRefAdvertisement()
|
|
|
|
|
if (manifest) c.runNextClassManifestAdvertisement()
|
|
|
|
|
case _ ⇒
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-09 07:31:41 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-05 14:38:48 +02:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[remote] object ArteryTransport {
|
|
|
|
|
|
2016-09-08 16:12:29 +02:00
|
|
|
val ProtocolName = "akka"
|
2016-05-27 16:45:48 +02:00
|
|
|
|
2016-05-05 14:38:48 +02:00
|
|
|
val Version = 0
|
2016-05-17 14:17:21 +02:00
|
|
|
|
2016-09-06 14:32:42 +02:00
|
|
|
class AeronTerminated(e: Throwable) extends RuntimeException(e)
|
|
|
|
|
|
|
|
|
|
object ShutdownSignal extends RuntimeException with NoStackTrace
|
|
|
|
|
|
|
|
|
|
def autoSelectPort(hostname: String): Int = {
|
2016-05-17 14:17:21 +02:00
|
|
|
val socket = DatagramChannel.open().socket()
|
|
|
|
|
socket.bind(new InetSocketAddress(hostname, 0))
|
|
|
|
|
val port = socket.getLocalPort
|
|
|
|
|
socket.close()
|
|
|
|
|
port
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|