problem: waits to long to reconnect (10 seconds)

solution: use exp backoff, from 100ms to 1 minute
This commit is contained in:
Igor Artamonov
2021-09-21 21:41:44 -04:00
parent 16e589ab69
commit 37e3d4e252

View File

@@ -33,6 +33,10 @@ import io.netty.buffer.Unpooled
import io.netty.handler.codec.http.HttpHeaderNames import io.netty.handler.codec.http.HttpHeaderNames
import org.reactivestreams.Publisher import org.reactivestreams.Publisher
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.util.backoff.BackOff
import org.springframework.util.backoff.BackOffExecution
import org.springframework.util.backoff.ExponentialBackOff
import org.springframework.util.backoff.FixedBackOff
import reactor.core.Disposable import reactor.core.Disposable
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@@ -78,13 +82,11 @@ class EthereumWsFactory(
private const val START_REQUEST = "{\"jsonrpc\":\"2.0\", \"method\":\"eth_subscribe\", \"id\":\"blocks\", \"params\":[\"newHeads\"]}" private const val START_REQUEST = "{\"jsonrpc\":\"2.0\", \"method\":\"eth_subscribe\", \"id\":\"blocks\", \"params\":[\"newHeads\"]}"
} }
var retryInterval = Defaults.retryConnection.seconds private var reconnectBackoff: BackOff = ExponentialBackOff().also {
set(value) { it.initialInterval = Duration.ofMillis(100).toMillis()
if (retryInterval <= 0) { it.maxInterval = Duration.ofMinutes(1).toMillis()
throw IllegalArgumentException("Reconnect interval cannot be zero or less: $retryInterval")
}
field = value
} }
private var currentBackOff = reconnectBackoff.start()
private val parser = ResponseWSParser() private val parser = ResponseWSParser()
@@ -106,6 +108,11 @@ class EthereumWsFactory(
private var connection: Disposable? = null private var connection: Disposable? = null
private val reconnecting = AtomicBoolean(false) private val reconnecting = AtomicBoolean(false)
fun setReconnectIntervalSeconds(value: Long) {
reconnectBackoff = FixedBackOff(value * 1000, FixedBackOff.UNLIMITED_ATTEMPTS)
currentBackOff = reconnectBackoff.start()
}
fun connect() { fun connect() {
if (keepConnection) { if (keepConnection) {
connectInternal() connectInternal()
@@ -127,13 +134,18 @@ class EthereumWsFactory(
.many() .many()
.unicast() .unicast()
.onBackpressureBuffer<JsonRpcRequest>() .onBackpressureBuffer<JsonRpcRequest>()
log.info("Reconnect to $uri in $retryInterval seconds...") val retryInterval = currentBackOff.nextBackOff()
if (retryInterval == BackOffExecution.STOP) {
log.warn("Reconnect backoff exhausted. Permanently closing the connection")
return
}
log.info("Reconnect to $uri in ${retryInterval}ms...")
Global.control.schedule( Global.control.schedule(
{ {
reconnecting.set(false) reconnecting.set(false)
connectInternal() connectInternal()
}, },
retryInterval, TimeUnit.SECONDS) retryInterval, TimeUnit.MILLISECONDS)
} }
private fun connectInternal() { private fun connectInternal() {
@@ -154,7 +166,6 @@ class EthereumWsFactory(
}, },
{ _, _ -> } { _, _ -> }
) )
.headers { headers -> .headers { headers ->
headers.add(HttpHeaderNames.ORIGIN, origin) headers.add(HttpHeaderNames.ORIGIN, origin)
basicAuth?.let { auth -> basicAuth?.let { auth ->
@@ -189,6 +200,9 @@ class EthereumWsFactory(
} }
fun handle(inbound: WebsocketInbound, outbound: WebsocketOutbound): Publisher<Void> { fun handle(inbound: WebsocketInbound, outbound: WebsocketOutbound): Publisher<Void> {
//restart backoff after connection
currentBackOff = reconnectBackoff.start()
val consumer = inbound val consumer = inbound
// Accept up to 15Mb messages, same config is used by Geth // Accept up to 15Mb messages, same config is used by Geth
.aggregateFrames(15 * 1024 * 1024) .aggregateFrames(15 * 1024 * 1024)