orbit.client.OrbitClient.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of orbit-client Show documentation
Show all versions of orbit-client Show documentation
Orbit is a system to make building highly scalable realtime services easier.
/*
Copyright (C) 2015 - 2019 Electronic Arts Inc. All rights reserved.
This file is part of the Orbit Project .
See license in LICENSE.
*/
package orbit.client
import kotlinx.coroutines.launch
import mu.KotlinLogging
import orbit.client.actor.ActorProxyFactory
import orbit.client.addressable.AddressableDefinitionDirectory
import orbit.client.addressable.AddressableProxyFactory
import orbit.client.addressable.CapabilitiesScanner
import orbit.client.addressable.InvocationSystem
import orbit.client.execution.ExecutionLeases
import orbit.client.execution.ExecutionSystem
import orbit.client.mesh.AddressableLeaser
import orbit.client.mesh.NodeLeaseRenewalFailed
import orbit.client.mesh.NodeLeaseRenewalFailedHandler
import orbit.client.mesh.NodeLeaser
import orbit.client.net.ClientAuthInterceptor
import orbit.client.net.ClientState
import orbit.client.net.ConnectionHandler
import orbit.client.net.GrpcClient
import orbit.client.net.LocalNode
import orbit.client.net.MessageHandler
import orbit.client.serializer.Serializer
import orbit.util.concurrent.SupervisorScope
import orbit.util.di.ComponentContainer
import orbit.util.time.Clock
import orbit.util.time.ConstantTicker
import orbit.util.time.stopwatch
import kotlin.coroutines.CoroutineContext
class OrbitClient(val config: OrbitClientConfig = OrbitClientConfig()) {
private val logger = KotlinLogging.logger { }
private val container = ComponentContainer()
val clock = Clock()
private val scope = SupervisorScope(
pool = config.pool,
exceptionHandler = this::onUnhandledException
)
private val ticker = ConstantTicker(
scope = scope,
targetTickRate = config.tickRate.toMillis(),
clock = clock,
logger = logger,
exceptionHandler = this::onUnhandledException,
autoStart = false,
onTick = this::tick
)
init {
container.configure {
instance(this@OrbitClient)
instance(config)
instance(scope)
instance(clock)
instance(LocalNode(config))
definition()
definition()
definition()
definition()
definition()
definition()
externallyConfigured(config.nodeLeaseRenewalFailedHandler)
definition()
definition()
definition()
definition()
definition()
externallyConfigured(config.addressableConstructor)
definition()
definition()
definition()
}
}
private val nodeLeaser by container.inject()
private val messageHandler by container.inject()
private val connectionHandler by container.inject()
private val capabilitiesScanner by container.inject()
private val localNode by container.inject()
private val definitionDirectory by container.inject()
private val executionSystem by container.inject()
private val nodeLeaseRenewalFailedHandler by container.inject()
val actorFactory by container.inject()
fun start() = scope.launch {
logger.info("Starting Orbit client...")
val (elapsed, _) = stopwatch(clock) {
// Flip state
localNode.manipulate {
it.copy(clientState = ClientState.CONNECTING)
}
// Scan for capabilities
capabilitiesScanner.scan()
definitionDirectory.setupDefinition(
interfaceClasses = capabilitiesScanner.addressableInterfaces,
impls = capabilitiesScanner.interfaceLookup
)
localNode.manipulate {
it.copy(capabilities = definitionDirectory.generateCapabilities())
}
// Get first lease
nodeLeaser.joinCluster()
// Open message channel
connectionHandler.connect()
localNode.manipulate {
it.copy(clientState = ClientState.CONNECTED)
}
// Start tick
ticker.start()
}
logger.info("Orbit client started successfully in {}ms.", elapsed)
}
private suspend fun tick() {
// Keep stream open
connectionHandler.tick()
// See if lease needs renewing
nodeLeaser.tick()
// Timeout messages etc
messageHandler.tick()
// Handle actor deactivations and leases
executionSystem.tick()
}
fun stop() = scope.launch {
logger.info("Stopping Orbit...")
val (elapsed, _) = stopwatch(clock) {
localNode.manipulate {
it.copy(clientState = ClientState.STOPPING)
}
// TODO: Wait until placements will stop
// Stop all addressables
executionSystem.stop()
// Stop messaging
connectionHandler.disconnect()
// Stop the tick
ticker.stop()
localNode.manipulate {
it.copy(clientState = ClientState.IDLE)
}
}
logger.info("Orbit stopped successfully in {}ms.", elapsed)
}
@Suppress("UNUSED_PARAMETER")
private fun onUnhandledException(coroutineContext: CoroutineContext, throwable: Throwable) =
onUnhandledException(throwable)
private fun onUnhandledException(throwable: Throwable) {
when (throwable) {
is NodeLeaseRenewalFailed -> {
logger.error { "Node lease renewal failed..." }
nodeLeaseRenewalFailedHandler.onLeaseRenewalFailed()
}
else -> logger.error(throwable) { "Unhandled exception in Orbit Client." }
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy