Scala 为什么我的异步控制器阻塞?

Scala 为什么我的异步控制器阻塞?,scala,playframework,concurrency,Scala,Playframework,Concurrency,一般来说,我不熟悉框架和scala。在尝试测试和理解同步和异步操作之间的差异时,请使用以下代码: package controllers import play.api.mvc._ import play.api.libs.concurrent.Execution.Implicits._ import scala.concurrent.Future object Application extends Controller { def async = Action.async {

一般来说,我不熟悉框架和scala。在尝试测试和理解同步和异步操作之间的差异时,请使用以下代码:

package controllers

import play.api.mvc._
import play.api.libs.concurrent.Execution.Implicits._
import scala.concurrent.Future

object Application extends Controller {

  def async = Action.async {
    Logger.info("async start")
    val resultF = Future {
      Thread.sleep(2000)
      Logger.info("async end")
      Ok
    }
    Logger.info("non-blocking")
    resultF
  }

  def sync = Action {
    Thread.sleep(2000)
    Ok
  }

}
运行应用程序时,我在浏览器中有10个选项卡请求“/异步”。我的期望是,所有请求都需要大约2秒的时间来填充,我将在日志中看到10个“异步开始”条目,然后是10个“异步结束”条目

然而,实际结果是看到“异步开始”、“异步结束”10次。直到上一个请求完成,下一个请求才开始。异步的执行似乎被阻塞了,根本无法处理并发请求

我的问题是,为什么系统会以这种方式运行,以及有哪些具体的更改来支持并发请求处理

代码运行良好

使用ab(ApacheBench)或其他方法发送并发请求

> ab -c 5 -n 5 localhost:9000/async
This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        
Server Hostname:        localhost
Server Port:            9000

Document Path:          /async
Document Length:        0 bytes

Concurrency Level:      5
Time taken for tests:   2.013 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      375 bytes
HTML transferred:       0 bytes
Requests per second:    2.48 [#/sec] (mean)
Time per request:       2013.217 [ms] (mean)
Time per request:       402.643 [ms] (mean, across all concurrent requests)
Transfer rate:          0.18 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:  2008 2011   2.0   2011    2013
Waiting:     2007 2011   2.0   2011    2013
Total:       2008 2011   2.0   2011    2013

Percentage of the requests served within a certain time (ms)
  50%   2011
  66%   2012
  75%   2012
  80%   2013
  90%   2013
  95%   2013
  98%   2013
  99%   2013
 100%   2013 (longest request)
>ab-c5-n5本地主机:9000/async
这是ApacheBench,版本2.3
版权1996亚当·特维斯,宙斯科技有限公司,http://www.zeustech.net/
授权给Apache软件基金会,http://www.apache.org/
对本地主机进行基准测试(耐心等待)…完成
服务器软件:
服务器主机名:localhost
服务器端口:9000
文档路径:/async
文档长度:0字节
并发级别:5
测试时间:2.013秒
完成申请:5
失败的请求:0
传输总量:375字节
已传输的HTML:0字节
每秒请求数:2.48[#秒](平均值)
每次请求的时间:2013.217[ms](平均值)
每个请求的时间:402.643[ms](所有并发请求的平均值)
传输速率:接收到0.18[千字节/秒]
连接时间(毫秒)
最小平均值[+/-sd]最大中值
连接:0.0.0
处理:2008 2011 2.0 2011 2013
等待:2007 2011 2.0 2011 2013
总数:2008-2011 2.0 2011-2013
在特定时间内服务的请求百分比(毫秒)
50%   2011
66%   2012
75%   2012
80%   2013
90%   2013
95%   2013
98%   2013
99%   2013
2013年100%(最长请求)
我发送了5个并发请求,所有请求都按预期结束(见上文测试所用时间:2.013秒)

代码运行正常

使用ab(ApacheBench)或其他方法发送并发请求

> ab -c 5 -n 5 localhost:9000/async
This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        
Server Hostname:        localhost
Server Port:            9000

Document Path:          /async
Document Length:        0 bytes

Concurrency Level:      5
Time taken for tests:   2.013 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      375 bytes
HTML transferred:       0 bytes
Requests per second:    2.48 [#/sec] (mean)
Time per request:       2013.217 [ms] (mean)
Time per request:       402.643 [ms] (mean, across all concurrent requests)
Transfer rate:          0.18 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:  2008 2011   2.0   2011    2013
Waiting:     2007 2011   2.0   2011    2013
Total:       2008 2011   2.0   2011    2013

Percentage of the requests served within a certain time (ms)
  50%   2011
  66%   2012
  75%   2012
  80%   2013
  90%   2013
  95%   2013
  98%   2013
  99%   2013
 100%   2013 (longest request)
>ab-c5-n5本地主机:9000/async
这是ApacheBench,版本2.3
版权1996亚当·特维斯,宙斯科技有限公司,http://www.zeustech.net/
授权给Apache软件基金会,http://www.apache.org/
对本地主机进行基准测试(耐心等待)…完成
服务器软件:
服务器主机名:localhost
服务器端口:9000
文档路径:/async
文档长度:0字节
并发级别:5
测试时间:2.013秒
完成申请:5
失败的请求:0
传输总量:375字节
已传输的HTML:0字节
每秒请求数:2.48[#秒](平均值)
每次请求的时间:2013.217[ms](平均值)
每个请求的时间:402.643[ms](所有并发请求的平均值)
传输速率:接收到0.18[千字节/秒]
连接时间(毫秒)
最小平均值[+/-sd]最大中值
连接:0.0.0
处理:2008 2011 2.0 2011 2013
等待:2007 2011 2.0 2011 2013
总数:2008-2011 2.0 2011-2013
在特定时间内服务的请求百分比(毫秒)
50%   2011
66%   2012
75%   2012
80%   2013
90%   2013
95%   2013
98%   2013
99%   2013
2013年100%(最长请求)

我发送了5个并发请求,所有请求都按预期结束(见上文
测试所用时间:2.013秒

使用
操作。async
并不自动表示您没有阻塞。这完全取决于您是否使用了阻塞API

Thread.sleep
在您的
未来
中是一个阻塞操作,但您并没有向
ExecutionContext
发出您正在执行的信号,因此行为将根据您使用的
ExecutionContext
以及您的机器有多少处理器而有所不同。您的代码在
ExecutionContext.global中正常工作

在这两种情况下,您都使用
Thread.sleep(2000)
来阻止线程

在这两种情况下,睡眠调用都发生在操作的线程池中(这不是最优的)

如《理解播放线程池》中所述:

Play框架自下而上是一个异步web框架。使用迭代对象异步处理流。游戏中的线程池被调整为使用比传统web框架中更少的线程,因为游戏核心中的IO永远不会阻塞

正因为如此,如果您计划编写阻塞IO代码,或者可能会执行大量CPU密集型工作的代码,那么您需要确切地知道哪个线程池正在承载该工作负载,并且需要相应地对其进行调优

在您的例子中,您只需在阻塞线程的两种情况下等待几秒钟,其中并行因子的默认设置为1

如果要阻止线程,可以使用以下方法:

 def async = Action.async {
    Logger.info("async start")
    val resultF = Future {
      blocking{
        Thread.sleep(2000)
        Logger.info("async end")
        Ok
      }
    }
    Logger.info("non-blocking")
    resultF
  }

使用
Action.async
并不自动意味着没有阻塞。这完全取决于您是否使用了阻塞API

Thread.sleep
在您的
未来
中是一个阻塞操作,但您并没有向
ExecutionContext
发出您正在执行的信号,因此行为将根据您使用的
ExecutionContext
以及您的机器有多少处理器而有所不同。您的代码在
ExecutionContext.global中正常工作

在这两种情况下,您都使用
Thread.sleep(2000)
来阻止线程

在这两种情况下,睡眠调用都发生在操作的线程池中(这不是最优的)

如《理解游戏》中所述