Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
5
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@akishin

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

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

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
5
Help us understand the problem. What are the problem?