- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 Akka HTTP 对我的请求进行基本身份验证。
碰巧我有一个外部资源要通过身份验证,所以我必须对这个资源进行休息调用。
这需要一些时间,在处理过程中,我的 API 的其余部分似乎被阻塞,等待这个调用。
我用一个非常简单的例子重现了这个:
// used dispatcher:
implicit val system = ActorSystem()
implicit val executor = system.dispatcher
implicit val materializer = ActorMaterializer()
val routes =
(post & entity(as[String])) { e =>
complete {
Future{
Thread.sleep(5000)
e
}
}
} ~
(get & path(Segment)) { r =>
complete {
"get"
}
}
最佳答案
你观察到的是预期的行为——当然,这是非常糟糕的。好的,已知的解决方案和最佳实践存在以防止它。在这个答案中,我想花一些时间来短、长和深入地解释这个问题——享受阅读吧!
简答 :“不要阻塞路由基础设施!”,始终使用专用调度程序进行阻塞操作!
观察到的症状的原因:问题是您正在使用 context.dispatcher
作为调度员,阻塞 future 继续执行。路由基础设施使用相同的调度程序(简单来说只是“一堆线程”)来实际处理传入的请求——因此,如果您阻止所有可用线程,最终会使路由基础设施挨饿。 (辩论和基准测试的一个问题是,如果 Akka HTTP 可以防止这种情况发生,我会将其添加到我的研究待办事项列表中)。
必须特别小心地处理阻塞,以免影响同一调度程序的其他用户(这就是为什么我们将执行分离到不同的用户如此简单的原因),如 Akka 文档部分所述:Blocking needs careful management .
我想在这里提请注意的另一件事是,如果可能的话,应该完全避免阻塞 API - 如果您长时间运行的操作实际上不是一个操作,而是一系列操作,则您可以将它们分离到不同的参与者或序列化的 future 。无论如何,只是想指出——如果可能,避免这种阻塞调用,但如果你必须——那么下面解释了如何正确处理这些调用。
深度分析及解决方案 :
现在我们知道出了什么问题,从概念上讲,让我们看看上面代码中到底有什么问题,以及这个问题的正确解决方案是怎样的:
颜色 = 线程状态:
1) [bad]
调度程序对错误代码的行为 :
// BAD! (due to the blocking in Future):
implicit val defaultDispatcher = system.dispatcher
val routes: Route = post {
complete {
Future { // uses defaultDispatcher
Thread.sleep(5000) // will block on the default dispatcher,
System.currentTimeMillis().toString // starving the routing infra
}
}
}
2) [good!]
调度员行为良好的结构化代码/调度员 :
application.conf
配置这个专用于阻塞行为的调度程序:
my-blocking-dispatcher {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
// in Akka previous to 2.4.2:
core-pool-size-min = 16
core-pool-size-max = 16
max-pool-size-min = 16
max-pool-size-max = 16
// or in Akka 2.4.2+
fixed-pool-size = 16
}
throughput = 100
}
ThreadPoolExecutor
它对阻塞操作保持可用的线程数有一个硬限制。大小设置取决于您的应用程序的功能以及您的服务器拥有的核心数。
// GOOD (due to the blocking in Future):
implicit val blockingDispatcher = system.dispatchers.lookup("my-blocking-dispatcher")
val routes: Route = post {
complete {
Future { // uses the good "blocking dispatcher" that we configured,
// instead of the default dispatcher – the blocking is isolated.
Thread.sleep(5000)
System.currentTimeMillis().toString
}
}
}
my-blocking-dispatcher-*
启动,并启动到配置的线程数。它处理所有的 Sleeping 在那里。此外,在这些线程上一段时间没有发生任何事情后,它会关闭它们。如果我们用另一组阻塞来攻击服务器,池将启动新线程来处理 sleep()-ing 它们,但与此同时 - 我们不会浪费我们宝贵的线程“只是呆在那里没做什么”。
3) [workaround-ish]
blocking
时的调度员行为正确应用 :
scala.concurrent.blocking
在遇到阻塞操作时可以提供帮助的方法。它通常会导致更多的线程被启动以在阻塞操作中幸存下来。
// OK, default dispatcher but we'll use `blocking`
implicit val dispatcher = system.dispatcher
val routes: Route = post {
complete {
Future { // uses the default dispatcher (it's a Fork-Join Pool)
blocking { // will cause much more threads to be spun-up, avoiding starvation somewhat,
// but at the cost of exploding the number of threads (which eventually
// may also lead to starvation problems, but on a different layer)
Thread.sleep(5000)
System.currentTimeMillis().toString
}
}
}
}
2)
, 为可用的阻塞操作提供一个调度程序,并在那里执行它们。
2.0.1
关于scala - Akka HTTP : Blocking in a future blocks the server,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34641861/
我有一些 Scala 代码,它用两个不同版本的类型参数化函数做了一些漂亮的事情。我已经从我的应用程序中简化了很多,但最后我的代码充满了形式 w(f[Int],f[Double]) 的调用。哪里w()是
如果我在同一目录中有两个单独的未编译的 scala 文件: // hello.scala object hello { def world() = println("hello world") }
val schema = df.schema val x = df.flatMap(r => (0 until schema.length).map { idx => ((idx, r.g
环境: Play 2.3.0/Scala 2.11.1/IntelliJ 13.1 我使用 Typesafe Activator 1.2.1 用 Scala 2.11.1 创建一个新项目。项目创建好后
我只是想知道如何使用我自己的类扩展 Scala 控制台和“脚本”运行程序,以便我可以通过使用实际的 Scala 语言与其通信来实际使用我的代码?我应将 jar 放在哪里,以便无需临时配置即可从每个 S
我已经根据 README.md 文件安装了 ensime,但是,我在低级 ensime-server 缓冲区中出现以下错误: 信息: fatal error :scala.tools.nsc.Miss
我正在阅读《Scala 编程》一书。在书中,它说“一个函数文字被编译成一个类,当在运行时实例化时它是一个函数值”。并且它提到“函数值是对象,因此您可以根据需要将它们存储在变量中”。 所以我尝试检查函数
我有 hello world scala native 应用程序,想对此应用程序运行小型 scala 测试我使用通常的测试命令,但它抛出异常: NativeMain.scala object Nati
有few resources在网络上,在编写与代码模式匹配的 Scala 编译器插件方面很有指导意义,但这些对生成代码(构建符号树)没有帮助。我应该从哪里开始弄清楚如何做到这一点? (如果有比手动构建
我是 Scala 的新手。但是,我用 创建了一个中等大小的程序。斯卡拉 2.9.0 .现在我想使用一个仅适用于 的开源库斯卡拉 2.7.7 . 是吗可能 在我的 Scala 2.9.0 程序中使用这个
有没有办法在 Scala 2.11 中使用 scala-pickling? 我在 sonatype 存储库中尝试了唯一的 scala-pickling_2.11 工件,但它似乎不起作用。我收到消息:
这与命令行编译器选项无关。如何以编程方式获取代码内的 Scala 版本? 或者,Eclipse Scala 插件 v2 在哪里存储 scalac 的路径? 最佳答案 这无需访问 scala-compi
我正在阅读《Scala 编程》一书,并在第 6 章中的类 Rational 实现中遇到了一些问题。 这是我的 Rational 类的初始版本(基于本书) class Rational(numerato
我是 Scala 新手,我正在尝试开发一个使用自定义库的小项目。我在库内创建了一个mysql连接池。这是我的库的build.sbt organization := "com.learn" name :
我正在尝试运行一些 Scala 代码,只是暂时打印出“Hello”,但我希望在 SBT 项目中编译 Scala 代码之前运行 Scala 代码。我发现在 build.sbt 中有以下工作。 compi
Here链接到 maven Scala 插件使用。但没有提到它使用的究竟是什么 Scala 版本。我创建了具有以下配置的 Maven Scala 项目: org.scala-tools
我对 Scala 还很陌生,请多多包涵。我有一堆包裹在一个大数组中的 future 。 future 已经完成了查看几 TB 数据的辛勤工作,在我的应用程序结束时,我想总结上述 future 的所有结
我有一个 scala 宏,它依赖于通过包含其位置的静态字符串指定的任意 xml 文件。 def myMacro(path: String) = macro myMacroImpl def myMacr
这是我的功能: def sumOfSquaresOfOdd(in: Seq[Int]): Int = { in.filter(_%2==1).map(_*_).reduce(_+_) } 为什么我
这个问题在这里已经有了答案: Calculating the difference between two Java date instances (45 个答案) 关闭 5 年前。 所以我有一个这
我是一名优秀的程序员,十分优秀!