从Finagle中的同步代码调用异步/未来代码

时间:2016-08-29 22:12:50

标签: scala asynchronous future finagle

我正在开发一个Finagle HTTP应用程序,在该应用程序中实现服务而不利用Futures并通过第三方库访问Redis。此类服务具有以下形式:

class SampleOldService extends Service[Request, Response] {
  def apply(req: Request): Future[Response] = {
    val value: Int = getValueFromRedis()
    val response: Response = buildResponse(value)
    Future.value(response)
  }
}

(它们比这复杂得多 - 这一点就是它们同步。)

在某些时候,我们开始使用Futures开发新服务,并使用Finagle Redis API。 Redis调用封装在Store类中。新服务具有以下形式:

class SampleNewService extends Service[Request, Response] {
  def apply(req: Request): Future[Response] = {
    val value: Future[Int] = Store.getValue()
    val response: Future[Response] = value map buildResponse
    response
  }
}

(它们比这复杂得多 - 这一点就是它们异步。)

我们开始重构旧服务,以利用异步性和期货。我们希望逐步完成此操作,而无需立即完全重新实现它们。

第一步是尝试使用新的Store类,代码如下:

class SampleOldService extends Service[Request, Response] {
  def apply(req: Request): Future[Response] = {
    val valueFuture: Future[Int] = Store.getValue()
    val value: Int = Await.result(valueFuture)
    val response: Response = buildResponse(value)
    Future.value(response)
  }
}

然而,它被证明是灾难性的,因为在重负载下,对旧服务的请求停留在Await.result()调用。新的异步服务没有任何问题。

问题似乎与线程耗尽和/或未来池有关。我们已经找到了几个关于如何通过使用自定义池(例如FuturePool)从异步调用执行同步调用(执行I / O)的解决方案,但不是相反的方法,是我们的情况。

那么,在Finagle中调用异步代码(执行I / O)的同步代码的推荐方法是什么?

2 个答案:

答案 0 :(得分:1)

您可以做的最简单的事情是使用返回Future的线程池包装同步调用。 Twitter的util-core提供了FuturePool实用程序来实现这一目标。

类似的东西(未经测试的代码):

import com.twitter.util.FuturePool

val future = FuturePool.unboundedPool {
  val result = myBlockingCall.await()
  result
}

答案 1 :(得分:1)

您可以使用FuturePool,它是在缓存线程池之上运行的期货,但为什么要这样做,让服务返回一个承诺,并在您从商店类完成未来时设置承诺的值。

val p: Promise[Response] = Promise[Response]()
val value: Future[Int] = Store.getValue()
value onSuccess {x => 
  val result: Response = buildResponse(x)
  p.setValue(result)
}
p