gpt4 book ai didi

scala - 喷雾.io : When (not) to use non-blocking route handling?

转载 作者:行者123 更新时间:2023-12-02 09:33:56 28 4
gpt4 key购买 nike

如果我们正在考虑生产级 REST API,我们是否应该尽可能使用非阻塞,例如

def insertDbAsync(rows: RowList): Future[Unit] = ...
...
val route =
path("database" / "insertRowList") {
post {
entity(as[RowList]) { rows =>
log.info(s"${rows.length} rows received")
val async = insertDbAsync(rows)
onComplete(async) {
case Success(response) =>
complete("success")
case Failure(t) =>
complete("error")
}
}
}
}

我认为答案很可能是"is",但是在决定什么应该和不应该是阻止代码时有哪些指导原则,为什么?

最佳答案

Spray 使用 Akka 作为底层平台,因此建议与 Actor 相同 ( Blocking Needs Careful Management )。阻塞代码可能需要太多线程,这可能:

  • 扼杀actor的轻量级:默认情况下,数百万个actor可能在一个线程上运行。例如,假设一个非阻塞参与者需要 0.001 个线程。一个被阻塞的 Actor(阻塞时间比平常多 100 倍)将平均占用 1 个线程(并不总是相同的线程)。首先,拥有的线程越多,释放的内存就越多,每个被阻塞的线程都保存在阻塞之前分配的完整调用堆栈,包括堆栈中的引用(因此 GC 无法删除它们)。其次,如果您有超过 number_of_processors 个线程 - 您将失去性能。第三,如果您使用一些动态池 - 添加新线程可能会花费大量时间。

  • 导致线程饥饿 - 您的池中可能充满了线程,但它们不执行任何操作 - 因此在阻塞操作完成之前无法处理新任务(0%CPU 负载,但有 100500 条消息等待处理)。甚至可能会导致死锁。然而,Akka 默认使用 Fork-Join-Pool,所以如果你的阻塞代码被管理(被 scala.concurrent.blocking 包围 - Await.result 内部有这样的包围) -它会通过创建新线程而不是阻塞线程的成本来防止饥饿,但它不会弥补其他问题。

  • 传统上会导致死锁,因此不利于设计

如果代码被外部阻塞,你可以用 future 包围它:

 import scala.concurrent._
val f = Future {
someNonBlockingCode()
blocking { //mark this thread as "blocked" so fork-join-pool may create another one to compensate
someBlocking()
}
}

内部单独 Actor :

 f pipeTo sender //will send the result to `sender` actor

内部喷涂路线:

 onComplete(f) { .. }

最好在单独的池/调度程序(基于 fork-join-pool)内执行此类 future。

附注作为 future 的替代方案(从设计角度来看它们可能不太方便),您可以考虑 Akka I/O , 继续/Coroutines , Actor 的pools (也在单独的调度程序内)、Disruptor 等

关于scala - 喷雾.io : When (not) to use non-blocking route handling?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29384347/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com