orbit.server.OrbitServer.kt Maven / Gradle / Ivy
/*
Copyright (C) 2015 - 2019 Electronic Arts Inc. All rights reserved.
This file is part of the Orbit Project .
See license in LICENSE.
*/
package orbit.server
import kotlinx.coroutines.launch
import mu.KotlinLogging
import orbit.server.auth.AuthSystem
import orbit.server.concurrent.RuntimePools
import orbit.server.concurrent.RuntimeScopes
import orbit.server.mesh.AddressableDirectory
import orbit.server.mesh.AddressableManager
import orbit.server.mesh.ClusterManager
import orbit.server.mesh.LocalNodeInfo
import orbit.server.mesh.NodeDirectory
import orbit.server.net.ConnectionManager
import orbit.server.net.RemoteMeshNodeManager
import orbit.server.pipeline.Pipeline
import orbit.server.pipeline.PipelineSteps
import orbit.server.pipeline.step.AuthStep
import orbit.server.pipeline.step.BlankStep
import orbit.server.pipeline.step.EchoStep
import orbit.server.pipeline.step.IdentityStep
import orbit.server.pipeline.step.PlacementStep
import orbit.server.pipeline.step.RoutingStep
import orbit.server.pipeline.step.TransportStep
import orbit.server.pipeline.step.VerifyStep
import orbit.server.router.Router
import orbit.server.service.AddressableManagementService
import orbit.server.service.ConnectionService
import orbit.server.service.GrpcEndpoint
import orbit.server.service.HealthCheckList
import orbit.server.service.HealthService
import orbit.server.service.NodeManagementService
import orbit.server.service.ServerAuthInterceptor
import orbit.shared.mesh.NodeStatus
import orbit.util.concurrent.ShutdownLatch
import orbit.util.di.ComponentContainer
import orbit.util.time.Clock
import orbit.util.time.ConstantTicker
import orbit.util.time.stopwatch
import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.CoroutineContext
class OrbitServer(private val config: OrbitServerConfig) {
constructor() : this(OrbitServerConfig())
private val logger = KotlinLogging.logger {}
private var shutdownLatch = AtomicReference()
private val runtimePools = RuntimePools(
cpuPool = config.cpuPool,
ioPool = config.ioPool
)
private val runtimeScopes = RuntimeScopes(
runtimePools = runtimePools,
exceptionHandler = this::onUnhandledException
)
private val container = ComponentContainer()
private val clock = Clock()
private val grpcEndpoint by container.inject()
private val localNodeInfo by container.inject()
private val nodeManager by container.inject()
private val nodeDirectory by container.inject()
private val addressableDirectory by container.inject()
private val pipeline by container.inject()
private val remoteMeshNodeManager by container.inject()
private val ticker = ConstantTicker(
scope = runtimeScopes.cpuScope,
targetTickRate = config.tickRate.toMillis(),
clock = clock,
logger = logger,
exceptionHandler = this::onUnhandledException,
autoStart = false,
onTick = this::tick
)
init {
container.configure {
instance(this@OrbitServer)
instance(config)
instance(runtimePools)
instance(runtimeScopes)
instance(clock)
instance(config.serverInfo)
// Service
definition()
definition()
definition()
definition()
definition()
definition()
definition()
// Net
definition()
// Pipeline
definition()
definition()
definition()
definition()
definition()
definition()
definition()
definition()
definition()
definition()
// Mesh
definition()
definition()
definition()
definition()
externallyConfigured(config.nodeDirectory)
externallyConfigured(config.addressableDirectory)
// Auth
definition()
// Router
definition()
}
}
fun start() = runtimeScopes.cpuScope.launch {
logger.info("Starting Orbit server...")
val (elapsed, _) = stopwatch(clock) {
// Start the pipeline
pipeline.start()
// Setup the local node information
localNodeInfo.start()
// Start tick
ticker.start()
// Start gRPC endpoint
// We shouldn't do this until we're ready to serve traffic
grpcEndpoint.start()
// Flip status to active
localNodeInfo.updateInfo {
it.copy(nodeStatus = NodeStatus.ACTIVE)
}
// Acquire the latch
if (config.acquireShutdownLatch) {
ShutdownLatch().also {
shutdownLatch.set(it)
it.acquire()
}
}
}
logger.info("Orbit server started successfully in {}ms.", elapsed)
}
fun stop() = runtimeScopes.cpuScope.launch {
logger.info("Stopping Orbit server...")
val (elapsed, _) = stopwatch(clock) {
// Flip status to draining
localNodeInfo.updateInfo {
it.copy(nodeStatus = NodeStatus.DRAINING)
}
// Stop gRPC
val grpcEndpoint by container.inject()
grpcEndpoint.stop()
// Stop the tick
ticker.stop()
// Stop pipeline
pipeline.stop()
// Flip status to draining
localNodeInfo.updateInfo {
it.copy(nodeStatus = NodeStatus.STOPPED)
}
// Release the latch
shutdownLatch.get()?.release().also {
shutdownLatch.set(null)
}
}
logger.info("Orbit server stopped successfully in {}ms.", elapsed)
}
private suspend fun tick() {
localNodeInfo.tick()
nodeManager.tick()
nodeDirectory.tick()
addressableDirectory.tick()
remoteMeshNodeManager.tick()
}
@Suppress("UNUSED_PARAMETER")
private fun onUnhandledException(coroutineContext: CoroutineContext, throwable: Throwable) =
onUnhandledException(throwable)
private fun onUnhandledException(throwable: Throwable) {
logger.error(throwable) { "Unhandled exception in Orbit." }
}
} © 2015 - 2025 Weber Informatics LLC | Privacy Policy