solution: metrics for proxy requests
This commit is contained in:
55
src/main/kotlin/io/emeraldpay/dshackle/ChainValue.kt
Normal file
55
src/main/kotlin/io/emeraldpay/dshackle/ChainValue.kt
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021 EmeraldPay, Inc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.emeraldpay.dshackle
|
||||||
|
|
||||||
|
import io.emeraldpay.api.proto.Common
|
||||||
|
import io.emeraldpay.grpc.Chain
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps a lazily created value associated with a Chain
|
||||||
|
*/
|
||||||
|
class ChainValue<V>(
|
||||||
|
private val factory: (chain: Chain) -> V
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val values = EnumMap<Chain, V>(Chain::class.java)
|
||||||
|
private val valuesInitLock = ReentrantLock()
|
||||||
|
|
||||||
|
fun get(chain: Common.ChainRef): V {
|
||||||
|
return get(Chain.byId(chain.number))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(chain: Chain): V {
|
||||||
|
val existing = values[chain]
|
||||||
|
if (existing != null) {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
return valuesInitLock.withLock {
|
||||||
|
// second check in case it was updated while getting the lock
|
||||||
|
val existing2 = values[chain]
|
||||||
|
if (existing2 != null) {
|
||||||
|
existing2
|
||||||
|
} else {
|
||||||
|
val created = factory(chain)
|
||||||
|
values[chain] = created
|
||||||
|
created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,12 +18,17 @@ package io.emeraldpay.dshackle.proxy
|
|||||||
|
|
||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||||
import io.emeraldpay.api.proto.Common
|
import io.emeraldpay.api.proto.Common
|
||||||
|
import io.emeraldpay.dshackle.ChainValue
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.TlsSetup
|
import io.emeraldpay.dshackle.TlsSetup
|
||||||
import io.emeraldpay.dshackle.config.ProxyConfig
|
import io.emeraldpay.dshackle.config.ProxyConfig
|
||||||
import io.emeraldpay.dshackle.rpc.NativeCall
|
import io.emeraldpay.dshackle.rpc.NativeCall
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
|
import io.emeraldpay.grpc.Chain
|
||||||
import io.infinitape.etherjar.rpc.RpcException
|
import io.infinitape.etherjar.rpc.RpcException
|
||||||
|
import io.micrometer.core.instrument.Counter
|
||||||
|
import io.micrometer.core.instrument.Metrics
|
||||||
|
import io.micrometer.core.instrument.Timer
|
||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
import io.netty.buffer.Unpooled
|
import io.netty.buffer.Unpooled
|
||||||
import io.netty.channel.ChannelHandler
|
import io.netty.channel.ChannelHandler
|
||||||
@@ -39,6 +44,7 @@ import reactor.netty.http.server.HttpServer
|
|||||||
import reactor.netty.http.server.HttpServerRequest
|
import reactor.netty.http.server.HttpServerRequest
|
||||||
import reactor.netty.http.server.HttpServerResponse
|
import reactor.netty.http.server.HttpServerResponse
|
||||||
import reactor.netty.http.server.HttpServerRoutes
|
import reactor.netty.http.server.HttpServerRoutes
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.function.BiFunction
|
import java.util.function.BiFunction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +62,8 @@ class ProxyServer(
|
|||||||
private val log = LoggerFactory.getLogger(ProxyServer::class.java)
|
private val log = LoggerFactory.getLogger(ProxyServer::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val chainMetrics = ChainValue { chain -> RequestMetrics(chain) }
|
||||||
|
|
||||||
private val errorHandler: ChannelHandler = object : ChannelHandler {
|
private val errorHandler: ChannelHandler = object : ChannelHandler {
|
||||||
override fun handlerAdded(p0: ChannelHandlerContext?) {
|
override fun handlerAdded(p0: ChannelHandlerContext?) {
|
||||||
}
|
}
|
||||||
@@ -123,9 +131,16 @@ class ProxyServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun processRequest(chain: Common.ChainRef, request: Mono<ByteArray>): Flux<ByteBuf> {
|
fun processRequest(chain: Common.ChainRef, request: Mono<ByteArray>): Flux<ByteBuf> {
|
||||||
return request.map(readRpcJson)
|
val metrics = chainMetrics.get(chain)
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
|
return request
|
||||||
|
.map(readRpcJson)
|
||||||
.flatMapMany { call -> execute(chain, call) }
|
.flatMapMany { call -> execute(chain, call) }
|
||||||
|
.doOnNext {
|
||||||
|
metrics.callMetric.record(System.currentTimeMillis() - startTime, TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
.onErrorResume(RpcException::class.java) { err ->
|
.onErrorResume(RpcException::class.java) { err ->
|
||||||
|
metrics.errorMetric.increment()
|
||||||
val id = err.details?.let {
|
val id = err.details?.let {
|
||||||
if (it is JsonRpcResponse.Id) it else JsonRpcResponse.NumberId(-1)
|
if (it is JsonRpcResponse.Id) it else JsonRpcResponse.NumberId(-1)
|
||||||
} ?: JsonRpcResponse.NumberId(-1)
|
} ?: JsonRpcResponse.NumberId(-1)
|
||||||
@@ -147,4 +162,13 @@ class ProxyServer(
|
|||||||
.send(results)
|
.send(results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RequestMetrics(chain: Chain) {
|
||||||
|
val callMetric = Timer.builder("request.jsonrpc.call")
|
||||||
|
.tag("chain", chain.chainCode)
|
||||||
|
.register(Metrics.globalRegistry)
|
||||||
|
val errorMetric = Counter.builder("request.jsonrpc.err")
|
||||||
|
.tag("chain", chain.chainCode)
|
||||||
|
.register(Metrics.globalRegistry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@ package io.emeraldpay.dshackle.rpc
|
|||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||||
import io.emeraldpay.api.proto.Common
|
import io.emeraldpay.api.proto.Common
|
||||||
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
||||||
|
import io.emeraldpay.dshackle.ChainValue
|
||||||
import io.emeraldpay.dshackle.SilentException
|
import io.emeraldpay.dshackle.SilentException
|
||||||
import io.emeraldpay.grpc.Chain
|
import io.emeraldpay.grpc.Chain
|
||||||
import io.micrometer.core.instrument.Counter
|
import io.micrometer.core.instrument.Counter
|
||||||
@@ -54,6 +55,7 @@ class BlockchainRpc(
|
|||||||
.register(Metrics.globalRegistry)
|
.register(Metrics.globalRegistry)
|
||||||
private val errorMetric = Counter.builder("request.grpc.err")
|
private val errorMetric = Counter.builder("request.grpc.err")
|
||||||
.register(Metrics.globalRegistry)
|
.register(Metrics.globalRegistry)
|
||||||
|
private val chainMetrics = ChainValue { chain -> RequestMetrics(chain) }
|
||||||
|
|
||||||
override fun nativeCall(request: Mono<BlockchainOuterClass.NativeCallRequest>): Flux<BlockchainOuterClass.NativeCallReplyItem> {
|
override fun nativeCall(request: Mono<BlockchainOuterClass.NativeCallRequest>): Flux<BlockchainOuterClass.NativeCallReplyItem> {
|
||||||
var startTime = 0L
|
var startTime = 0L
|
||||||
@@ -61,7 +63,7 @@ class BlockchainRpc(
|
|||||||
return nativeCall.nativeCall(
|
return nativeCall.nativeCall(
|
||||||
request
|
request
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
metrics = getMetrics(it.chain)
|
metrics = chainMetrics.get(it.chain)
|
||||||
metrics!!.nativeCallMetric.increment()
|
metrics!!.nativeCallMetric.increment()
|
||||||
startTime = System.currentTimeMillis()
|
startTime = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
@@ -73,14 +75,14 @@ class BlockchainRpc(
|
|||||||
override fun subscribeHead(request: Mono<Common.Chain>): Flux<BlockchainOuterClass.ChainHead> {
|
override fun subscribeHead(request: Mono<Common.Chain>): Flux<BlockchainOuterClass.ChainHead> {
|
||||||
return streamHead.add(
|
return streamHead.add(
|
||||||
request
|
request
|
||||||
.doOnNext { getMetrics(it.type).subscribeHeadMetric.increment() }
|
.doOnNext { chainMetrics.get(it.type).subscribeHeadMetric.increment() }
|
||||||
).doOnError { errorMetric.increment() }
|
).doOnError { errorMetric.increment() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun subscribeTxStatus(request: Mono<BlockchainOuterClass.TxStatusRequest>): Flux<BlockchainOuterClass.TxStatus> {
|
override fun subscribeTxStatus(request: Mono<BlockchainOuterClass.TxStatusRequest>): Flux<BlockchainOuterClass.TxStatus> {
|
||||||
return request.flatMapMany { request ->
|
return request.flatMapMany { request ->
|
||||||
val chain = Chain.byId(request.chainValue)
|
val chain = Chain.byId(request.chainValue)
|
||||||
val metrics = getMetrics(chain)
|
val metrics = chainMetrics.get(chain)
|
||||||
metrics.subscribeTxMetric.increment()
|
metrics.subscribeTxMetric.increment()
|
||||||
try {
|
try {
|
||||||
trackTx.find { it.isSupported(chain) }?.let { track ->
|
trackTx.find { it.isSupported(chain) }?.let { track ->
|
||||||
@@ -99,7 +101,7 @@ class BlockchainRpc(
|
|||||||
override fun subscribeBalance(requestMono: Mono<BlockchainOuterClass.BalanceRequest>): Flux<BlockchainOuterClass.AddressBalance> {
|
override fun subscribeBalance(requestMono: Mono<BlockchainOuterClass.BalanceRequest>): Flux<BlockchainOuterClass.AddressBalance> {
|
||||||
return requestMono.flatMapMany { request ->
|
return requestMono.flatMapMany { request ->
|
||||||
val chain = Chain.byId(request.asset.chainValue)
|
val chain = Chain.byId(request.asset.chainValue)
|
||||||
val metrics = getMetrics(chain)
|
val metrics = chainMetrics.get(chain)
|
||||||
metrics.subscribeBalanceMetric.increment()
|
metrics.subscribeBalanceMetric.increment()
|
||||||
val asset = request.asset.code.toLowerCase()
|
val asset = request.asset.code.toLowerCase()
|
||||||
try {
|
try {
|
||||||
@@ -122,7 +124,7 @@ class BlockchainRpc(
|
|||||||
override fun getBalance(requestMono: Mono<BlockchainOuterClass.BalanceRequest>): Flux<BlockchainOuterClass.AddressBalance> {
|
override fun getBalance(requestMono: Mono<BlockchainOuterClass.BalanceRequest>): Flux<BlockchainOuterClass.AddressBalance> {
|
||||||
return requestMono.flatMapMany { request ->
|
return requestMono.flatMapMany { request ->
|
||||||
val chain = Chain.byId(request.asset.chainValue)
|
val chain = Chain.byId(request.asset.chainValue)
|
||||||
val metrics = getMetrics(chain)
|
val metrics = chainMetrics.get(chain)
|
||||||
metrics.getBalanceMetric.increment()
|
metrics.getBalanceMetric.increment()
|
||||||
val asset = request.asset.code.toLowerCase()
|
val asset = request.asset.code.toLowerCase()
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
@@ -156,30 +158,6 @@ class BlockchainRpc(
|
|||||||
.doOnError { errorMetric.increment() }
|
.doOnError { errorMetric.increment() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val metrics = EnumMap<Chain, RequestMetrics>(Chain::class.java)
|
|
||||||
private val metricsSetupLock = ReentrantLock()
|
|
||||||
|
|
||||||
private fun getMetrics(chain: Common.ChainRef): RequestMetrics {
|
|
||||||
return getMetrics(Chain.byId(chain.number))
|
|
||||||
}
|
|
||||||
private fun getMetrics(chain: Chain): RequestMetrics {
|
|
||||||
val existing = metrics[chain]
|
|
||||||
if (existing != null) {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
return metricsSetupLock.withLock {
|
|
||||||
// second check in case it was updated while getting the lock
|
|
||||||
val existing2 = metrics[chain]
|
|
||||||
if (existing2 != null) {
|
|
||||||
existing2
|
|
||||||
} else {
|
|
||||||
val created = RequestMetrics(chain)
|
|
||||||
metrics[chain] = created
|
|
||||||
created
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RequestMetrics(chain: Chain) {
|
class RequestMetrics(chain: Chain) {
|
||||||
val nativeCallMetric = Counter.builder("request.grpc.request")
|
val nativeCallMetric = Counter.builder("request.grpc.request")
|
||||||
.tag("type", "nativeCall")
|
.tag("type", "nativeCall")
|
||||||
|
|||||||
Reference in New Issue
Block a user