Search code examples
scalaserverclientfinagletwitter-finagle

How to make a finagle client automatic try to reconnect if the server goes down?


I'm testing finagle and got over the current situation:

Server:

import com.twitter.finagle.{Http, Service}
import com.twitter.finagle.http
import com.twitter.util.{Await, Future}

class Server(name: String, port: Int) {
  val service = new Service[http.Request, http.Response] {
    def apply(req: http.Request): Future[http.Response] = {
      println(s"Request on server $name")
      Future.value(
        http.Response(req.version, http.Status.Ok)
      )
    }
  }

  val server = Http.serve(s":$port", service)
}

object Server1 extends App {
  val server = new Server("1", 9001).server
  Await.ready(server)
}

object Server2 extends App {
  val server = new Server("2", 9002).server
  Await.ready(server)
}

Client:

import com.twitter.finagle.{Http, Service}
import com.twitter.finagle.http
import com.twitter.util.{Await, Future}

object Client extends App {

  val client: Service[http.Request, http.Response] =
    Http.client
      .methodBuilder("localhost:9001,localhost:9002")
      .newService("client")

  while(true) {
    val request = http.Request(http.Method.Get, "/")
    request.host = "www.scala-lang.org"
    val response: Future[http.Response] = client(request)
    println(Await.result(response))
    Thread.sleep(50)
  }
}

Finagle version:

libraryDependencies ++= Seq(
  "com.twitter" %% "finagle-core" % "20.4.1",
  "com.twitter" %% "finagle-http" % "20.4.1",
  "com.twitter" %% "finagle-serversets" % "20.4.1"
)
  • I start the two servers and then start the client
  • The client sends requests to the server
  • I Kill one of the servers
  • The client dies immediately

I couldn't find a way to make the client not die in this situation and try to reconnect to the server when it is up again


Solution

  • Not sure if this exactly meets requirement, going by documentation 2 configurations seems applicable. By adding RetryBudget and FailureAccrualPolicy should make it close to your requirement. Hope following code will provide an idea.

    val budget = RetryBudget(
      ttl = 10.seconds,
      minRetriesPerSec = 5,
      percentCanRetry = 0.1
    )
    
    val client: Service[http.Request, http.Response] = Http.client
        .methodBuilder("localhost:9001,localhost:9002")
        .withRetryBudget(budget)
        .withRetryBackoff(Backoff.exponentialJittered(2.seconds, 32.seconds))
        .configured(Param(() => FailureAccrualPolicy.successRate(
          requiredSuccessRate = 0.95,
          window = 100,
          markDeadFor = Backoff.const(10.seconds))))
        .newService("client")