Reputation: 896
I have written an API that takes couple of minutes to create a response. This apparently results in connection resets:
curl: (56) Recv failure: Connection reset by peer
I have tried following timeout configurations from Akka HTTP but nothing seems to solve the above issue.
akka.http.server.idle-timeout = 600 s
akka.http.client.idle-timeout = 600 s
akka.http.server.request-timeout = 600 s
I suspect the issue is with the idle-timeout
. But the above configuration doesn't seem to take any affect.
Could anyone please suggest what could be the issue?
I have tried a reverse scenario where i have set the idle-timeout
to 5s
which is suppose to reset the connection but even that doesn't work. Not sure what is really wrong here. Could anyone please explain this behaviour and how can I fix it?
I have implemented a fully working example for anyone to try here.
application.conf
akka.http.server.idle-timeout = 5 s
akka.http.client.idle-timeout = 5 s
akka.http.server.request-timeout = 600 s
helloworld {
http {
interface = "127.0.0.1"
port = "8066"
}
}
Routes.scala
package com.codingkapoor.helloworld.http
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.util.Timeout
import akka.stream.ActorMaterializer
import scala.concurrent.duration._
private[helloworld] trait Routes extends JsonSupport {
implicit lazy val timeout: Timeout = Timeout(5.minute)
implicit def materializer: ActorMaterializer
lazy val routes: Route = pathSingleSlash {
get {
Thread.sleep(180000)
complete(StatusCodes.OK, "Hello World!")
}
}
}
Upvotes: 2
Views: 4108
Reputation: 896
There were two mistakes I found, fixing which solved the problem.
Mistake 1
I was forgetting to pass appConf
as an argument while creating an instance of ActorSystem
, without which ActorSystem
was always getting created with default akka configs. Hence always make sure to pass appConf
while creating an instance of ActorSystem
otherwise your akka configurations will never take effect.
HelloWorld.Scala
val appConf = RuntimeEnvironment.appConf
implicit val system = ActorSystem("helloworld", appConf)
Mistake 2
In the question I was putting Thread.sleep
in the Route thread. This was causing idle-timeout
to reach the logs only after sleep got over.
What I am trying to say is that when I was creating ActorSystem
without appConf
, default idle-timeout
of 1 min
was supposed to come into play but because I was putting Thread.sleep
of 3 min
in the route thread, the idle-timeout
of 1 min
was getting logged only after 3 mins
.
# Despite idle-timeout being 1 min, the following exception was getting
# logged only after 3 mins
Caused by: akka.http.impl.engine.HttpIdleTimeoutException:
HTTP idle-timeout encountered, no bytes passed in the last 1 minute.
This is configurable by akka.http.[server|client].idle-timeout.
The best way to mimic delayed response is to complete the request with a future as shown below. Here I have set an idle-timeout
of 4 mins
and request-timeout
of 3 mins
. This results in request being served always! Hope that clarifies.
application.conf
akka.http.server.idle-timeout = 240000 s
akka.http.client.idle-timeout = 240000 s
akka.http.server.request-timeout = 600 s
Routes.scala
lazy val routes: Route = pathSingleSlash {
get {
complete {
Future.unit.map(_ => Thread.sleep(180000))
.map(_ => StatusCodes.OK -> "Hello World!")
}
}
}
The whole idea is that if idle-timeout
is greater than request-timeout
, your requests at least have chance to get served otherwise they would simply get dropped!
idle timeout (10mins) ------>
request timeout (5 mins) --->
idle timeout (1 min) --->
request timeout (5 mins) ------>
You can find the working solution here.
Upvotes: 3