solution: extended monitoring option; monitoring for upstream selection filter
This commit is contained in:
@@ -31,6 +31,8 @@ class Global {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
var metricsExtended = false
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val objectMapper: ObjectMapper = createObjectMapper()
|
val objectMapper: ObjectMapper = createObjectMapper()
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class MonitoringConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var enableJvm: Boolean = true
|
var enableJvm: Boolean = true
|
||||||
|
var enableExtended: Boolean = false
|
||||||
|
|
||||||
data class PrometheusConfig(
|
data class PrometheusConfig(
|
||||||
val enabled: Boolean,
|
val enabled: Boolean,
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ class MonitoringConfigReader: YamlConfigReader(), ConfigReader<MonitoringConfig>
|
|||||||
}
|
}
|
||||||
val prometheus = readPrometheus(getMapping(input, "prometheus"))
|
val prometheus = readPrometheus(getMapping(input, "prometheus"))
|
||||||
return MonitoringConfig(enabled, prometheus).also { conf ->
|
return MonitoringConfig(enabled, prometheus).also { conf ->
|
||||||
getValueAsBool(input, "enableJVM")?.let { conf.enableJvm = it }
|
getValueAsBool(input, "JVM")?.let { conf.enableJvm = it }
|
||||||
|
getValueAsBool(input, "jvm")?.let { conf.enableJvm = it }
|
||||||
|
getValueAsBool(input, "extended")?.let { conf.enableExtended = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
package io.emeraldpay.dshackle.monitoring
|
package io.emeraldpay.dshackle.monitoring
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpServer
|
import com.sun.net.httpserver.HttpServer
|
||||||
import io.emeraldpay.dshackle.config.MainConfig
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.config.MonitoringConfig
|
import io.emeraldpay.dshackle.config.MonitoringConfig
|
||||||
import io.micrometer.core.instrument.Meter
|
import io.micrometer.core.instrument.Meter
|
||||||
import io.micrometer.core.instrument.Metrics
|
import io.micrometer.core.instrument.Metrics
|
||||||
@@ -32,9 +32,7 @@ import io.micrometer.prometheus.PrometheusConfig
|
|||||||
|
|
||||||
import io.micrometer.prometheus.PrometheusMeterRegistry
|
import io.micrometer.prometheus.PrometheusMeterRegistry
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.beans.factory.annotation.Value
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import javax.annotation.PostConstruct
|
import javax.annotation.PostConstruct
|
||||||
|
|
||||||
@@ -69,6 +67,9 @@ class MonitoringSetup(
|
|||||||
ProcessorMetrics().bindTo(Metrics.globalRegistry)
|
ProcessorMetrics().bindTo(Metrics.globalRegistry)
|
||||||
JvmThreadMetrics().bindTo(Metrics.globalRegistry)
|
JvmThreadMetrics().bindTo(Metrics.globalRegistry)
|
||||||
}
|
}
|
||||||
|
if (monitoringConfig.enableExtended) {
|
||||||
|
Global.metricsExtended = true
|
||||||
|
}
|
||||||
|
|
||||||
if (monitoringConfig.prometheus.enabled) {
|
if (monitoringConfig.prometheus.enabled) {
|
||||||
// use standard JVM server with a single thread blocking processing
|
// use standard JVM server with a single thread blocking processing
|
||||||
|
|||||||
@@ -16,11 +16,20 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.upstream
|
package io.emeraldpay.dshackle.upstream
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.grpc.Chain
|
||||||
|
import io.micrometer.core.instrument.DistributionSummary
|
||||||
|
import io.micrometer.core.instrument.Metrics
|
||||||
|
import io.micrometer.core.instrument.Tag
|
||||||
import org.reactivestreams.Subscriber
|
import org.reactivestreams.Subscriber
|
||||||
import reactor.core.publisher.EmitterProcessor
|
import reactor.core.publisher.EmitterProcessor
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.locks.Lock
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@@ -28,9 +37,10 @@ import kotlin.math.roundToLong
|
|||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class FilteredApis(
|
class FilteredApis(
|
||||||
|
val chain: Chain,
|
||||||
private val allUpstreams: List<Upstream>,
|
private val allUpstreams: List<Upstream>,
|
||||||
private val matcher: Selector.Matcher,
|
private val matcher: Selector.Matcher,
|
||||||
pos: Int,
|
private val pos: Int,
|
||||||
/**
|
/**
|
||||||
* Limit of retries
|
* Limit of retries
|
||||||
*/
|
*/
|
||||||
@@ -42,6 +52,8 @@ class FilteredApis(
|
|||||||
private const val DEFAULT_DELAY_STEP = 100
|
private const val DEFAULT_DELAY_STEP = 100
|
||||||
private const val MAX_WAIT_MILLIS = 5000L
|
private const val MAX_WAIT_MILLIS = 5000L
|
||||||
|
|
||||||
|
private const val metricsCode = "select"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <T> startFrom(upstreams: List<T>, pos: Int): List<T> {
|
fun <T> startFrom(upstreams: List<T>, pos: Int): List<T> {
|
||||||
return if (upstreams.size <= 1 || pos == 0) {
|
return if (upstreams.size <= 1 || pos == 0) {
|
||||||
@@ -51,14 +63,19 @@ class FilteredApis(
|
|||||||
upstreams.subList(safePosition, upstreams.size) + upstreams.subList(0, safePosition)
|
upstreams.subList(safePosition, upstreams.size) + upstreams.subList(0, safePosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val metrics = EnumMap<Chain, Monitoring>(Chain::class.java)
|
||||||
|
private val metricsSetup: Lock = ReentrantLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(allUpstreams: List<Upstream>,
|
constructor(chain: Chain,
|
||||||
|
allUpstreams: List<Upstream>,
|
||||||
matcher: Selector.Matcher,
|
matcher: Selector.Matcher,
|
||||||
pos: Int) : this(allUpstreams, matcher, pos, 10, 7)
|
pos: Int) : this(chain, allUpstreams, matcher, pos, 10, 7)
|
||||||
|
|
||||||
constructor(allUpstreams: List<Upstream>,
|
constructor(chain: Chain,
|
||||||
matcher: Selector.Matcher) : this(allUpstreams, matcher, 0, 10, 10)
|
allUpstreams: List<Upstream>,
|
||||||
|
matcher: Selector.Matcher) : this(chain, allUpstreams, matcher, 0, 10, 10)
|
||||||
|
|
||||||
private val delay: Int
|
private val delay: Int
|
||||||
private val standardUpstreams: List<Upstream>
|
private val standardUpstreams: List<Upstream>
|
||||||
@@ -87,6 +104,30 @@ class FilteredApis(
|
|||||||
.plus(standardUpstreams)
|
.plus(standardUpstreams)
|
||||||
.plus(fallbackUpstreams)
|
.plus(fallbackUpstreams)
|
||||||
|
|
||||||
|
if (Global.metricsExtended) {
|
||||||
|
getMetrics(chain).let { monitoring ->
|
||||||
|
monitoring.countStd.record(standardUpstreams.size.toDouble())
|
||||||
|
monitoring.countFallback.record(fallbackUpstreams.size.toDouble())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMetrics(chain: Chain): Monitoring {
|
||||||
|
val existing = metrics[chain]
|
||||||
|
return if (existing == null) {
|
||||||
|
metricsSetup.withLock {
|
||||||
|
val existingDoubleCheck = metrics[chain]
|
||||||
|
if (existingDoubleCheck != null) {
|
||||||
|
existingDoubleCheck
|
||||||
|
} else {
|
||||||
|
val created = Monitoring(chain)
|
||||||
|
metrics[chain] = created
|
||||||
|
created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
existing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun waitDuration(rawn: Long): Duration {
|
fun waitDuration(rawn: Long): Duration {
|
||||||
@@ -110,9 +151,16 @@ class FilteredApis(
|
|||||||
.delaySubscription(waitDuration(r + 1))
|
.delaySubscription(waitDuration(r + 1))
|
||||||
}.let { Flux.concat(it) }
|
}.let { Flux.concat(it) }
|
||||||
|
|
||||||
Flux.concat(first, retries)
|
var result = Flux.concat(first, retries)
|
||||||
.filter(Upstream::isAvailable)
|
|
||||||
.filter(matcher::matches)
|
if (Global.metricsExtended) {
|
||||||
|
var count = 0
|
||||||
|
result = result
|
||||||
|
.doOnNext { count++ }
|
||||||
|
.doFinally { metrics[chain]?.tried?.record(count.toDouble()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
result.filter { up -> up.isAvailable() && matcher.matches(up) }
|
||||||
.zipWith(control)
|
.zipWith(control)
|
||||||
.map { it.t1 }
|
.map { it.t1 }
|
||||||
.subscribe(subscriber)
|
.subscribe(subscriber)
|
||||||
@@ -132,4 +180,19 @@ class FilteredApis(
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Filter API: ${allUpstreams.size} upstreams with $matcher"
|
return "Filter API: ${allUpstreams.size} upstreams with $matcher"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Monitoring(chain: Chain) {
|
||||||
|
val countStd: DistributionSummary = DistributionSummary.builder("$metricsCode.exist")
|
||||||
|
.description("Count of available upstreams to select")
|
||||||
|
.tags(listOf(Tag.of("chain", chain.chainCode), Tag.of("role", "std")))
|
||||||
|
.register(Metrics.globalRegistry)
|
||||||
|
val countFallback: DistributionSummary = DistributionSummary.builder("$metricsCode.exist")
|
||||||
|
.description("Count of available fallback upstreams to select")
|
||||||
|
.tags(listOf(Tag.of("chain", chain.chainCode), Tag.of("role", "fallback")))
|
||||||
|
.register(Metrics.globalRegistry)
|
||||||
|
val tried: DistributionSummary = DistributionSummary.builder("$metricsCode.tried")
|
||||||
|
.description("How many upstreams were checked")
|
||||||
|
.tags(listOf(Tag.of("chain", chain.chainCode)))
|
||||||
|
.register(Metrics.globalRegistry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -69,18 +69,25 @@ abstract class Multistream(
|
|||||||
init {
|
init {
|
||||||
UpstreamAvailability.values().forEach { status ->
|
UpstreamAvailability.values().forEach { status ->
|
||||||
Metrics.gauge("$metrics.availability",
|
Metrics.gauge("$metrics.availability",
|
||||||
listOf(Tag.of("chain", chain.chainCode), Tag.of("status", status.name.toLowerCase()))
|
listOf(Tag.of("chain", chain.chainCode), Tag.of("status", status.name.toLowerCase())), this) {
|
||||||
, this) {
|
|
||||||
upstreams.count { it.getStatus() == status }.toDouble()
|
upstreams.count { it.getStatus() == status }.toDouble()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Metrics.gauge("$metrics.connected",
|
||||||
|
listOf(Tag.of("chain", chain.chainCode)), this) {
|
||||||
|
upstreams.size.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
upstreams.forEach { up ->
|
upstreams.forEach { up ->
|
||||||
Metrics.gauge("$metrics.lag",
|
monitorUpstream(up)
|
||||||
listOf(Tag.of("chain", chain.chainCode), Tag.of("upstream", up.getId()))
|
}
|
||||||
, this) {
|
}
|
||||||
up.getLag().toDouble()
|
|
||||||
}
|
private fun monitorUpstream(upstream: Upstream) {
|
||||||
|
Metrics.gauge("$metrics.lag",
|
||||||
|
listOf(Tag.of("chain", chain.chainCode), Tag.of("upstream", upstream.getId())), upstream) {
|
||||||
|
it.getLag().toDouble()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +109,7 @@ abstract class Multistream(
|
|||||||
upstreams.add(upstream)
|
upstreams.add(upstream)
|
||||||
onUpstreamsUpdated()
|
onUpstreamsUpdated()
|
||||||
setHead(updateHead())
|
setHead(updateHead())
|
||||||
|
monitorUpstream(upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeUpstream(id: String) {
|
fun removeUpstream(id: String) {
|
||||||
@@ -119,7 +127,7 @@ abstract class Multistream(
|
|||||||
if (seq >= Int.MAX_VALUE / 2) {
|
if (seq >= Int.MAX_VALUE / 2) {
|
||||||
seq = 0
|
seq = 0
|
||||||
}
|
}
|
||||||
return FilteredApis(upstreams, matcher, i)
|
return FilteredApis(chain, upstreams, matcher, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user