Scala真的是异步的

时间:2015-07-30 07:46:53

标签: node.js scala asynchronous java-8 nonblocking

这些天我正在研究Scala编写一个非阻塞的Restful API。我已经在node.js中完成了几个项目,并且这次很想看看Scala。

到目前为止,我的发现是在Scala中编写100%异步代码是不可能的,因为大多数底层代码都是阻塞的。您可以在Scala应用程序中找到有关如何使用JDBC进行数据库连接的教程。 JDBC被阻塞意味着任何使用它的应用程序都无法在单线程环境中工作,因为它会在等待数据库响应时排队所有其他请求。播放框架建议在使用阻塞API时增加线程池,这意味着我们将返回从我们开始的地方开始。

对于node.js,几乎所有可用模块都是异步写入的。特别是所有数据库连接器和文件处理程序都异步读/写,请记住,当您有一个线程时,没有任何东西可以阻塞。然而,我看到人们喜欢Scala而不是node.js,因为它的类型安全性和计算能力是异步的。但是异步行为并没有反映在网上可用的大部分教程和资源中。

我的问题是,只有我才能理解这一点,或者在JVM世界中是否存在混淆实际意味着什么?

更新

我知道同步的JDBC不能使Scala同步,但我看到有人认为他们的API是非阻塞的,但他们使用的是同步库。此外,如果大多数驱动程序和库仍然阻塞,如何在Scala中编写非阻塞应用程序。即使有替代方法,这意味着您在使用库检查其内部以查看其阻塞或非阻塞时是否必须非常小心,这与node.js不同。

3 个答案:

答案 0 :(得分:3)

有可能(我甚至会说很简单)在Scala(和Java)中编写异步应用程序但你必须小心关于第三方库,知道哪些是API:阻止和异步。

作为一个例子,Java标准库提供了阻塞和非阻塞IO API。

JDBC是本质上阻塞的API的另一个常见示例,如果您想编写100%异步应用程序,则只需为数据库使用不同的客户端。

与node.js的一个重大区别是,您实际上可以处理阻塞API:s和异步内容。这是可能的,因为我们在JVM中有多线程,并且可以在针对该任务调整的单独线程池中运行阻塞内容。因此,如果我们有8个可能的阻塞数据库连接,我们可以在具有8个线程的线程池上运行那些,而不会阻塞“常规”线程池上的任何异步代码。

作为旁注,我们有并发性还允许我们每个核心有一个线程来运行并行代码而不是整个vm中的异步代码。

答案 1 :(得分:1)

完全可以在Scala中编写完全异步代码。有很多反应库,TypeSafe投入大量精力来提供反应平台/生态系统。要说服自己,只需查看awesome scala并搜索" Reactive",或按照TypeSafe webinars

我不了解Node.js,但我认为你可以编写这样的阻止函数:

function aBlockingFunction() {
  while(resourceNotAvailable) {
  }
  ...
}

Scala不会神奇地使同步代码异步,但它将为您提供完全编写异步代码所需的一切(Futures / PromisesActors,Streams,... )。

异步并不意味着单线程,这意味着你在等待回复时继续做有用的思考。 Play被设计为完全异步,但你仍然可以编写阻塞代码(比如调用同步API)。但如果你完全写异步,是的,你可以运行一个线程。要做到这一点,只需使用Futures / Actors /...

Scala并发性的建模方式是:有一个(实际上很多)线程池对应于并行执行,还有任务要做(Futures / Actors / ...)。任务在可用线程上分派。当任务需要等待回复或产生时,该线程被分配给另一个准备好的任务,依此类推。您的计算似乎是"单线程"实际上是由很多小的协调迷你任务组成的,这些任务可以在任何可用的线程上运行(通常不一样)。

默认情况下,Play thread pools have as many threads as available processors。这是完美的,你的代码是异步的,因为一个线程永远不会空闲(当一个任务空闲时,另一个将替换它)。但是,在执行阻止操作时,您将阻止踏板,因此在操作完成之前,您将减少一个处理器。

这是众所周知的合作/先发制人问题。合作更轻,更快,因为它不需要保存那么多的上下文(它可以像函数调用一样轻)但是一个任务可以阻止其他任务。

这就是为什么当使用大量阻塞调用时,Play建议增加线程池(实际上你应该创建另一个专门用于阻塞操作的线程池)。理想情况下,您应该分配" number_of_simultaneous_blocking_operations + number_of_possible_parallel_executions"线程,以便总有一个线程可用于就绪任务。

我的建议是:寻找异步机制(Futures / Actors / Streams / ...)和反应库。

答案 2 :(得分:0)

Scala构建于JVM之上,JVM世界中的大多数代码(包括JDBC)都是为Java编写的。 Scala试图鼓励你使用Futures或Akka Actors编写非阻塞代码,但显然就像在任何编程语言中一样,你可以做你想做的事。

所以对于你的问题,没有scala不是100%非阻塞我的意思是你甚至可以使用Java中的synchronized块。但是你会受到鼓励,并且考虑到阻止代码的缺点以及如何避免代码(不可变,无状态等),并且你可以通过语言中的功能编写非阻塞代码(monads用于转换数据而不是更改数据)一种可变的方式)。