Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

akishin
プログラマです。 最近は PHP とか Azure とか触ってます。 ちょっと前まで Perl とか Ruby を使ってました。 更に前は Java と C# を使ってました。
http://d.hatena.ne.jp/akishin999/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした