解释Play中非阻塞IO的工作原理

时间:2018-01-10 11:15:26

标签: multithreading playframework future

我发现这篇文章解释了非阻塞IO Play框架:https://engineering.linkedin.com/play/play-framework-async-io-without-thread-pool-and-callback-hell

该文章中的代码示例

object ProxyController extends Controller {

  def proxy = Action {
    val responseFuture: Future[Response] = WS.url("http://example.com").get()

    Logger.info("Before map")
    val resultFuture: Future[Result] = responseFuture.map { resp =>
      Logger.info("Within map")
      // Create a Result that uses the http status, body, and content-type 
      // from the example.com Response
      Status(resp.status)(resp.body).as(resp.ahcResponse.getContentType)
    }
    Logger.info("After map")    

    Async(resultFuture)
}

该文章的解释:

  

引擎盖下,Play使用一个大小为每个CPU一个线程的线程池   核心。其中一个稀缺线程T1执行代理操作,   从上到下遍历代码,除了内容   传递给map方法的函数,因为它依赖于a   尚未完成的非阻塞I / O调用。一旦T1返回   AsyncResult,它继续处理其他请求。 后来的时候   来自example.com的响应终于可用,另一个线程T2   (可能与T1相同或不同)执行传递的函数   到地图方法。任何线程都没有被阻止   等待example.com的回复。

我不明白该段中突出显示的内容。 T1已返回线程池,应用程序如何跟踪并从example.com接收响应,然后提交线程T2以执行map函数。

请有人解释一下。

1 个答案:

答案 0 :(得分:2)

  

其中一个稀缺线程T1执行代理操作,从上到下运行代码,除了传递给map方法的函数内容,因为这取决于非阻塞I / O调用尚未完成。

此时已创建一个名为 future 的对象。

  

一旦T1返回AsyncResult,它就会继续处理其他请求。

当某些IO进入时,未来将在内存中等待实现。同时线程T1可以处理其他请求。

  

稍后,当example.com的响应最终可用时,

当响应可用时,这将调用一些内部Play代码,该代码读取响应并更改 future 的状态以赋予其值。在未来获得价值的那一刻,它会安排其 map 代码运行。这将在线程池中的某个线程中运行,例如T2。

  

另一个线程T2(可能与T1相同或不同)执行传递给map方法的函数。

     

任何一个线程都没有阻止等待来自example.com的响应。

Play不是通过阻塞线程来等待IO,而是秘密地注册了一个回调,这样当IO可用时它将实现未来。当未来完成时,它会自动调用应用程序代码的下一部分。这意味着应用程序代码可以等待(不使用线程),直到IO可用。