【问题标题】:If you don't block on a future, can it cause a race like condition?如果您不阻止未来,它会导致类似比赛的情况吗?
【发布时间】:2015-12-11 03:31:50
【问题描述】:

假设我有一个控制器,它有一个未来的调用,然后我重定向到另一个页面,该页面期望该调用已经计算。是否有可能未来返回的速度不够快,当我重定向到另一个页面时,数据不会“新鲜”?

例子:

class HomeController  {

  def action1 = Action{

    val f1 = Future { ... }
    Redirect(routes.HomeController.action2)
   }

  def action2 = Action {

     // expects the call to f1:Future to have executed
   }
}

我问的原因是我的服务层应该返回 Future 还是阻塞?或者我应该像在我的控制器中那样将 Future 传递给调用代码。

UserService
  save
  delete
  update
  findById
  etc. etc..

这些应该返回 Future 吗?

如果我希望调用已计算,我必须阻止。那么这方面没有硬性规定吗?

【问题讨论】:

    标签: scala playframework future


    【解决方案1】:

    你的问题有几处有点切题,所以我会依次解决

    1) 重定向响应会在未来返回之前返回吗?

    无法判断,而且它的行为也不会始终如一。对于在 Future 完成其计算之前不返回的重定向,您应该使用异步操作,以便在 Future 完成之前调用不会响应重定向:

    def action1 = Action.async { request =>
      futureComputation().map { _ => Redirect(routes.HomeController.action2) }
    }
    

    2) 我的服务层是否应该返回Future

    简单的答案是是的,如果底层 API 也是非阻塞的

    更细微的答案是是的,即使您的服务调用被阻塞,但您的工作流程中使用的所有其他调用都会返回一个未来。第二个条件是关于构图和可读性。

    假设工作流程是:

    • findById
    • fetchHttpResource(异步获取)
    • update(带有来自 fetch 的数据

    因为只有一个异步组件,工作流应该是异步的,你可以这样写:

    def workflow(id:UUID):Future[Unit] = {
      val user = UserService.findById(id)
      fetchHttpResource(id).map { resource =>
        val updatedUser = user.copy(x = resource)
        UserService.update(updatedUser)
      }
    }
    

    如果 UserService 也返回 Future,您可以使用 for-comprehension 代替:

    def workflow(id: UUID): Future[Unit] = for {
      user <- UserService.findById(id)
      resource <- fetchHttpResource(id)
      _ <- UserService.update(user.copy(x = resource))
    } yield {}
    

    这让我想到了

    3) 是否有使用Future 的硬性规定?

    异步代码是一个乌龟一直向下的事情。一旦你的链中有一个异步调用,你的链必须返回一个Future,一旦你有这个要求,通过formapflatMap等组成Future 签名中的错误处理变得更加清晰。额外的好处是,如果您现在有一个阻塞调用,也许将来您可以在该服务中找到一个异步 API 来使用,您的签名不必更改。

    要从可能失败的阻塞调用中返回Future,最好的策略是用future {} 包装它们。但是,如果您计算的数据不会失败,则使用Future.successful() 是更好的选择。

    【讨论】:

    • Future[Unit] 有什么意义,只是为了确保所有调用都返回未来,就像你所说的一致性和未来兼容性? (顺便说一句,你教的是一个新短语'turtles all the way down')。
    • 我以为所有的动作现在都是 Action.asyc 在 Play 中?
    • 正确,Future[Unit] 允许在完成时采取行动,无论是通过map/flatmap/for 组合还是通过onComplete/onSuccess/onFailure/recover 终止
    • Action 仍然是Request =&gt; Result 的函数,异步,即Request =&gt; Future[Result] 仍然是Action.async 的显式调用,而不是隐式的Action.apply
    【解决方案2】:

    按照您的代码,您的 action1 将启动将来执行的代码,然后将重定向发送到浏览器。不能保证将来的代码会在调用 action2 之前完成。

    但是,假设这是播放框架,您可以等待将来的代码完成而不会阻塞当前线程,方法是使用 Future 上的 map 或 onComplete 方法注册回调,您将在其中完成请求发送重定向。您需要将 Action 更改为 Action.async。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-01
      • 2016-09-23
      • 2017-12-29
      • 1970-01-01
      • 2011-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多