2

I wish to create a service where it makes an HTTP request to a specific URL, and if it doesn't get a result in 1 second, that request will timeout and then it will retry with another request, for maximum of 3 retries.

How to implement this in scala?

I'm looking in the documentation of Akka HTTP and Play's WSClient but I can't find it gets mention anywhere.

Note: In the case that if the request makes a side-effect on the server, I want the unsuccessful request to produce NO side-effect. How can this behavior be achieved? Is it even possible?

Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
TheOutsider
  • 63
  • 1
  • 10
  • you can use Akka Actors with akka Http with ASK call pattern and set the timeout as 1 second in the case of timeout just restart your child actor in supervison stratgy and again send the new request. I think that's what you are looking for – Raman Mishra Jan 25 '19 at 05:38
  • @Raman Mishra Could you elaborate on your comment? Also, what is supervision strategy? Thank you. – TheOutsider Jan 25 '19 at 05:42
  • You may strict your httpResponse with `.toStrict` and pass timeOut of `1 sec`.. In the Failure write your retry logic.. Here is the example https://www.programcreek.com/scala/akka.http.scaladsl.model.HttpRequest . Just for reference https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/basic-directives/toStrictEntity.html . I hope this helps. – Raj Parekh Jan 25 '19 at 08:25

2 Answers2

1

You can also use retry from akka patterns:

import akka.pattern.{ask, pipe, retry}
import akka.actor.{Actor, ActorSystem, Props, Scheduler}
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContext, Future}

class ClientActor extends Actor { //actor which times out 2 times and succeeds 3rd time

    var attempts = 0

    def receive = {
      case "request" =>
        this.attempts = attempts + 1
        if (attempts < 3) {
          Thread.sleep(2000)
          Future.failed(new Exception("timed out")) pipeTo sender
        } else {
          Thread.sleep(500)
          Future.successful(s"Successful in $attempts attempt") pipeTo sender
        }
        println(s"Attempt: $attempts")
    }
}


val system = ActorSystem("system") //actor system and needed implicits
implicit val ec: ExecutionContext = system.dispatcher 
implicit val timeout: Timeout = Timeout(1 seconds)
implicit val scheduler: Scheduler = system.scheduler


val client = system.actorOf(Props[ClientActor], "client-actor")

val future = retry(() => client ? "request", 3, 1 second) //create future which would retry 3 times

println(Await.result(future, 10 seconds)) //Would write "Successful in 3 attempt"
Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
0

As i understand your question what you need to do is:

steps

  • End point to access some API via HTTP call.
  • If the response is not coming within 1 second you need to get an exception.
  • In the case of exception you need to restart the service and again send the request.

you can do that in collaboration of Akka Http and Akka Actors.

With the use of Akka actors you can tell your service what need to be done when you are getting TimeoutException. You can make your API call via Akka ask pattern. If you will see the documnetation of Akka ask pattern here. it takes Akka ask timeout which you can set to any value you want, In case when you are not getting the response back in timeout you will get AkkaAskTimeOutException, which will be catched by your child actor and then it will be passed to the supervisor actor, While Supervisor actor catchs an exception we can use the supervison strategy and can specify what needs to be done Like (restart, shutdown, resume etc).

Supervison strategy: you can read about it here. Actually following is basic structure of an akka actor based application.

Supervisor Actor (we write supervison strategy in supervisor) and it has child actors.

child1(business logic) child2 (business logic)

Raman Mishra
  • 2,635
  • 2
  • 15
  • 32