Scala 为什么我的异步控制器阻塞?
一般来说,我不熟悉框架和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 {
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)
来阻止线程
在这两种情况下,睡眠调用都发生在操作的线程池中(这不是最优的)
如《理解游戏》中所述