我相信Erlang社区并不羡慕Node.js,因为它本身可以进行非阻塞I / O,并且可以轻松地将部署扩展到多个处理器(甚至没有内置在Node.js中) . 更多细节见http://journal.dedasys.com/2010/04/29/erlang-vs-node-js和Node.js or Erlang
那么Haskell呢? Haskell能否提供Node.js的一些好处,即一个干净的解决方案,以避免在不使用多线程编程的情况下阻止I / O?
Node.js有很多吸引人的东西
-
事件:没有线程操作,程序员只提供回调(如在Snap框架中)
-
保证回调可以在一个线程中运行:不存在竞争条件 .
-
漂亮而简单的UNIX友好API . 好处:优秀的HTTP支持 . DNS也可用 .
-
默认情况下,每个I / O都是异步的 . 这样可以更容易地避免锁定 . 但是,回调中过多的CPU处理会影响其他连接(在这种情况下,任务应分成较小的子任务并重新调度) .
-
客户端和服务器端的语言相同 . (我不了解服务器端和客户端之间的共享代码在实践中是如何有用的 . )
-
所有这些都包装在一个产品中 .
7 回答
好的,所以看了@gawi指出的一些node.js presentation,我可以更多地谈谈Haskell与node.js的比较 . 在演示中,Ryan描述了Green Threads的一些好处,但接着说他并不反对他的立场,特别是在Haskell的上下文中:我认为线程提供的抽象对于制作服务器代码至关重要更容易做对,更健壮 . 特别是:
fork
. 如果您正在实现的协议非常复杂,那么同时管理多个客户端的状态机会非常棘手,而线程则只允许您与单个客户端进行通信编写脚本 . 代码更容易正确,更易于理解和维护 .单个OS线程上的
回调是协作式多任务处理,而不是抢占式多任务处理,这是线程所能获得的 . 合作多任务处理的主要缺点是程序员负责确保没有饥饿 . 它失去了模块性:在一个地方犯了一个错误,它可以搞砸整个系统 . 这真的是你不想担心的事情,抢占就是简单的解决方案 . 而且,回调之间的通信是不可能的(它会死锁) .
并发在Haskell中并不难,因为大多数代码都是纯粹的,因此通过构造是线程安全的 . 有简单的通信原语 . 在Haskell中使用并发性比在具有不受限制的副作用的语言中使用自己的脚更难 .
是的,实际上事件和线程在Haskell中是统一的 .
您可以在显式轻量级线程中编程(例如,在一台笔记本电脑上有数百万个线程) .
或;您可以基于可伸缩事件通知以异步事件驱动的方式进行编程 .
线程实际上是implemented in terms of events,并且可以跨多个核心运行,具有无缝的线程迁移,具有记录的性能和应用程序 .
例如 . 对于
大规模concurrent job orchestration
并发集合scaling on 32 or 48 cores
工具支持分析和调试multi-threaded/multi-event programs .
高性能event-driven web servers .
有趣的用户:such as high-frequency trading .
32个核心上的并发集合nbody
在Haskell中,您既有事件又有线程,因为它是引擎盖下的所有事件 .
Read the paper描述了实施 .
首先,我不认为node.js正在做正确的事情暴露所有这些回调 . 你最终用CPS编写程序(继续传递样式),我认为应该是编译器完成转换的工作 .
因此,考虑到这一点,如果您愿意,可以使用异步样式进行编写,但通过这样做,您将错过以高效的同步样式编写,每个请求只有一个线程 . Haskell在同步代码方面非常有效,特别是与其他语言相比时 . 这是下面的所有事件 .
你可能还有竞争条件node.js,但它更难 .
每个请求都在它自己的线程中 . 当你编写必须与其他线程通信的代码时,由于haskell的并发原语,使其成为线程安全非常简单 .
看看hackage,看看自己 .
你没有这样的问题,ghc将在真实的OS线程中分配你的工作 .
哈斯克尔不可能在这里获胜......对吧?再想一想,http://www.haskell.org/haskellwiki/Haskell_in_web_browser .
下载ghc,点燃cabal . 每个需要都有一个包 .
我个人看到Node.js和回调编程是不必要的低级别和有点不自然的事情 . 为什么在良好的运行时(例如在GHC中找到的运行时)可以为您处理回调并且非常有效地执行回调时使用回调进行编程?
与此同时,GHC运行时有了很大的改进:它现在有一个名为MIO的"new new IO manager",其中"M"代表我相信的多核 . 它 Build 在现有IO管理器的基础之上,其主要目标是克服4核性能下降的原因 . 本文提供的性能数字令人印象深刻 . 看你自己:
和:
Mio已经进入GHC 7.8.1版本 . 我个人认为这是Haskell性能的重大进步 . 比较以前的GHC版本和7.8.1编译的现有Web应用程序性能将非常有趣 .
这个问题非常荒谬,因为1)Haskell已经以更好的方式解决了这个问题,并且2)以与Erlang大致相同的方式解决了这个问题 . 以下是针对节点的基准:http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks
为Haskell提供4个内核,它可以在单个应用程序中每秒执行100k(简单)请求 . 节点不能做多少,也无法跨核心扩展单个应用程序 . 而且你不需要做任何事情来收获它,因为Haskell运行时是非阻塞的 . Erlang是唯一一种内置于运行时的非阻塞IO的其他(相对常见)语言 .
恕我直言的事件是好的,但通过回调编程不是 .
使Web应用程序编码和调试变得特殊的大多数问题都来自使它们具有可扩展性和灵活性的因素 . 最重要的是HTTP的无状态特性 . 这增强了可导航性,但这会导致控制反转,其中IO元素(在本例中为Web服务器)调用应用程序代码中的不同处理程序 . 这个事件模型 - 或回调模型,更准确地说 - 是一场噩梦,因为回调不共享变量范围,并且导航的直观视图丢失 . 当用户来回导航以及其他问题时,很难防止所有可能的状态改变 .
可以说问题类似于GUI编程,其中事件模型工作正常,但GUI没有导航,也没有后退按钮 . 这使Web应用程序中可能的状态转换倍增 . 解决这些问题的尝试的结果是具有复杂配置的繁重框架,具有大量普遍的魔术标识符而不会质疑问题的根源:回调模型及其固有的缺乏共享变量范围,并且没有排序,因此序列必须通过链接标识符构建 .
有基于顺序的框架,如ocsigen(ocaml)海边(smalltalk)WASH(已停产,Haskell)和mflow(Haskell),它们在保持导航性和REST-fulness的同时解决了状态管理问题 . 在这些框架内,程序员可以将导航表达为命令序列,其中程序发送页面并在单个线程中等待响应,变量在范围内,后退按钮自动工作 . 这固有地产生更短,更安全,更可读的代码程序员可以清楚地看到导航 . (公平警告:我是mflow的开发者)
就像nodejs has dropped libev Snap Haskell Web Framework has dropped libev一样 .