Reputation: 1101
Is there a simple way to get the response time for a request to a url (other than keeping track of time in the code separately)?
import dispatch._, Defaults._
import scala.util.{Failure, Success}
val svc = dispatch.url(url)
val response: Future[com.ning.http.client.Response] = Http(svc > (x => x))
response onComplete {
case Success(content) => {
println(s"SUCCESS: ${content.getStatusCode()} for $url")
//how long did this take??
}
case Failure(t) => {
println(s"ERROR: timeout/failure for $url")
}
}
Upvotes: 1
Views: 1047
Reputation: 9100
This is a different approach for computing the elapsed time, might need a bit more customization (like handling of failure, NonFatal
most probably), but I think this is a bit more idiomatic. It works only with Future
s though.
First create a class for the result with time (could be replaced with a tuple too) and one for the failure with time.
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
case class ResultWithTime[ResultType](val result: ResultType, val time: Long)
class FailureWithTime(failure: Throwable, val time: Long) extends Throwable(failure)
Then create a method that wraps the future to execute and handles the case when it fails (might use Promise
if you do not like the Future
's map
and recoverWith
methods (as they require an implicit ExecutionContext
)).
object TimerFuture {
def apply[ResultType](fut: =>Future[ResultType]): Future[ResultWithTime[ResultType]] = {
val startTime = System.currentTimeMillis
fut.map(r => ResultWithTime(r, System.currentTimeMillis - startTime))
.recoverWith{case t:Throwable =>
Future.failed(new FailureWithTime(t, System.currentTimeMillis - startTime))
}
}
}
Here is an example how to use it:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.io.StdIn
object Y extends App {
TimerFuture(Future{Thread.sleep(200); "Hello"})
.onSuccess({case ResultWithTime(r, t) => println(s"Took $t ms to get: $r")})
TimerFuture(Future{Thread.sleep(100); throw new NullPointerException()})
.onFailure({case f: FailureWithTime => println(s"Took ${f.time} ms to get: ${f.getCause}")})
StdIn.readLine()
}
Upvotes: 1
Reputation: 9100
You can wrap your code with something like this (not tested):
class Timer[ResultType](computation: =>Future[ResultType]) {
private val startTime = System.currentTimeMillis
val result: Future[ResultType] = computation
private var endTime: Option[Long] = None
result.onComplete{case _ => endTime = Some(System.currentTimeMillis)}
def responseTime: Option[Long] = endTime.map(_ - startTime)
//probably also provide onComplete and other Future methods wrapping result.
}
object Timer{
def apply[ResultType](computation: =>Future[ResultType]) = {
new Timer(computation)
}
}
You can create more specific timers with factory methods in companion objects to help working with the computations you want to measure.
Usage:
val timedComputation = Timer{Http(svc > (x => x))}
timedComputation.result.onComplete{case _ => ???}
//In a completed event:
val responseTimeInMillis: Long = timedComputation.responseTime.get
Upvotes: 0