LoginSignup
6
5

More than 5 years have passed since last update.

Finagle で時間のかかる処理をタイムアウトさせる

Last updated at Posted at 2016-02-22

Finagle で時間のかかる処理を一定時間でタイムアウトさせる方法を調べたのでメモ。

環境

試した環境は以下。

build.sbt
name := "finagle-example"
scalaVersion := "2.11.7"

resolvers += "twitter-repo" at "https://maven.twttr.com"

libraryDependencies ++= Seq(
  "com.twitter" %% "finagle-core" % "6.31.+",
  "com.twitter" %% "finagle-http" % "6.31.+"
)

TimeoutFilter

よく使う処理という事だからか標準で com.twitter.finagle.service.TimeoutFilter が提供されている。

但し、以下のように単純に使ってみたら期待通りにタイムアウトしてくれない。

import com.twitter.conversions.time._
import com.twitter.finagle.Service
import com.twitter.finagle.service.TimeoutFilter
import com.twitter.finagle.util.DefaultTimer
import com.twitter.util.{Await, Future}
import com.twitter.logging.Logger

class HelloService extends Service[String, String] {
  override def apply(name: String): Future[String] = {
    Thread.sleep(3000)
    Future.value(s"Hello, $name.")
  }
}

object Example {

  private val log = Logger.get(getClass)

  def main(args: Array[String]):Unit = {
    val hello = new HelloService
    val timeoutFilter = new TimeoutFilter[String, String](1.seconds, DefaultTimer.twitter)

    val client = timeoutFilter andThen hello
    val f = client("Finagle")

    f.onSuccess { message =>
      log.info(s"result: ${message}")
    }.onFailure { err: Throwable =>
      log.error(err.toString)
    }
    Await.ready(f)
  }
}

実行してみる。

% sbt run
[info] Set current project to finagle-example (in build file:/home/akishin/src/scala/finagle_ex/)
[info] Updating {file:/home/akishin/src/scala/finagle_ex/}finagle_ex...
[info] Resolving jline#jline;2.12.1 ...
[info] Done updating.
[info] Compiling 1 Scala source to /home/akishin/src/scala/finagle_ex/target/scala-2.11/classes...
[info] Running Example 
2 22, 2016 10:41:27 午後 Example$$anonfun$main$1 apply
INFO: result: Hello, Finagle.
[success] Total time: 24 s, completed 2016/02/22 22:41:27

指定した時間を過ぎているのに onSuccess に入って来ている事が判る。

これは以下のように ServiceFuturePool.unboundedPool を使って処理を行うようにすると動作する。

import com.twitter.conversions.time._
import com.twitter.finagle.Service
import com.twitter.finagle.service.TimeoutFilter
import com.twitter.finagle.util.DefaultTimer
import com.twitter.util.{Await, Future, FuturePool}
import com.twitter.logging.Logger

class HelloService extends Service[String, String] {
  override def apply(name: String): Future[String] = FuturePool.unboundedPool {
    Thread.sleep(3000)
    "Hello, $name."
  }
}

object Example {

  private val log = Logger.get(getClass)

  def main(args: Array[String]):Unit = {
    val hello = new HelloService
    val timeoutFilter = new TimeoutFilter[String, String](1.seconds, DefaultTimer.twitter)

    val client = timeoutFilter andThen hello
    val f = client("Finagle")

    f.onSuccess { message =>
      log.info(s"result: ${message}")
    }.onFailure { err: Throwable =>
      log.error(err.toString)
    }
    Await.ready(f)
  }
}

実行してみる。

% sbt run
[info] Set current project to finagle-example (in build file:/home/akishin/src/scala/finagle_ex/)
[info] Compiling 1 Scala source to /home/akishin/src/scala/finagle_ex/target/scala-2.11/classes...
[info] Running Example 
2 22, 2016 10:45:28 午後 Example$$anonfun$main$2 apply
ERROR: com.twitter.finagle.IndividualRequestTimeoutException: exceeded 1.seconds to unspecified while waiting for a response for an individual request, excluding retries
[success] Total time: 6 s, completed 2016/02/22 22:45:29

今度は期待通りタイムアウトし、onFailure に入ってきてくれた。

Future#raiseWithin

TimeoutFilter を使わずに Future#raiseWithin でタイムアウトさせる事もできるっぽい。

この場合も TimeoutFilter と同じく以下では期待通りには動かない。

import com.twitter.conversions.time._
import com.twitter.finagle.Service
import com.twitter.finagle.util.DefaultTimer
import com.twitter.util.{Await, Future}
import com.twitter.logging.Logger

class HelloService extends Service[String, String] {
  override def apply(name: String): Future[String] = {
    Thread.sleep(3000)
    Future.value(s"Hello, $name.")
  }
}

object Example {

  private val log = Logger.get(getClass)

  def main(args: Array[String]):Unit = {
    val hello = new HelloService

    val f = hello("Finagle").raiseWithin(1.seconds)(DefaultTimer.twitter)

    f.onSuccess { message =>
      log.info(s"result: ${message}")
    }.onFailure { err: Throwable =>
      log.error(err.toString)
    }
    Await.ready(f)
  }
}

実行してみる。

% sbt run
[info] Set current project to finagle-example (in build file:/home/akishin/src/scala/finagle_ex/)
[info] Compiling 1 Scala source to /home/akishin/src/scala/finagle_ex/target/scala-2.11/classes...
[info] Running Example 
2 22, 2016 11:00:24 午後 Example$$anonfun$main$1 apply
INFO: result: Hello, Finagle.
[success] Total time: 8 s, completed 2016/02/22 23:00:24

やはり指定した時間を過ぎているのに onSuccess に入ってしまう。

先ほどと同じく FuturePool.unboundedPool を使うように書き換えてみる。

import com.twitter.conversions.time._
import com.twitter.finagle.Service
import com.twitter.finagle.util.DefaultTimer
import com.twitter.util.{Await, Future, FuturePool}
import com.twitter.logging.Logger

class HelloService extends Service[String, String] {
  override def apply(name: String): Future[String] = FuturePool.unboundedPool {
    Thread.sleep(3000)
    "Hello, $name."
  }
}

object Example {

  private val log = Logger.get(getClass)

  def main(args: Array[String]):Unit = {
    val hello = new HelloService

    val f = hello("Finagle").raiseWithin(1.seconds)(DefaultTimer.twitter)

    f.onSuccess { message =>
      log.info(s"result: ${message}")
    }.onFailure { err: Throwable =>
      log.error(err.toString)
    }
    Await.ready(f)
  }
}

実行してみる。

% sbt run
[info] Set current project to finagle-example (in build file:/home/akishin/src/scala/finagle_ex/)
[info] Compiling 1 Scala source to /home/akishin/src/scala/finagle_ex/target/scala-2.11/classes...
[info] Running Example 
2 22, 2016 11:01:56 午後 Example$$anonfun$main$2 apply
ERROR: com.twitter.util.TimeoutException: 1.seconds
[success] Total time: 6 s, completed 2016/02/22 23:01:56

例外の種類は先ほどと異なるが、こちらも期待通りタイムアウトし、onFailure に入ってきてくれた。

参考

TimeoutFilter - com.twitter.finagle.service.TimeoutFilter
https://twitter.github.io/finagle/docs/#com.twitter.finagle.service.TimeoutFilter

Introduction to Finagle
https://meta.plasm.us/slides/finagle/base/#17

Future - com.twitter.util.Future
https://twitter.github.io/util/docs/#com.twitter.util.Future

com.twitter.util.Futureのメモ - Qiita
http://qiita.com/mather314/items/6873a54e856c440afd1d

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5