什么是Haskell对Node.js的响应?

gaw*_*awi 215 concurrency multithreading haskell node.js

我相信Erlang社区并不羡慕Node.js,因为它本身可以进行非阻塞I/O,并且可以轻松地将部署扩展到多个处理器(甚至没有内置在Node.js中).有关详情,请访问http://journal.dedasys.com/2010/04/29/erlang-vs-node-jsNode.js或Erlang

那么Haskell呢?Haskell能否提供Node.js的一些好处,即一个干净的解决方案,以避免在不使用多线程编程的情况下阻止I/O?


Node.js有很多吸引人的东西

  1. 事件:没有线程操作,程序员只提供回调(如在Snap框架中)
  2. 保证回调在单个线程中运行:不存在竞争条件.
  3. 简单易用的UNIX友好API.好处:优秀的HTTP支持.DNS也可用.
  4. 默认情况下,每个I/O都是异步的.这样可以更容易地避免锁定.但是,回调中过多的CPU处理会影响其他连接(在这种情况下,任务应分成较小的子任务并重新调度).
  5. 客户端和服务器端的语言相同.(但是我没有看到这个值太多了.jQuery和Node.js共享事件编程模型,但其余部分非常不同.我只是看不出服务器端和客户端之间的共享代码如何在实践中很有用.)
  6. 所有这些都包装在一个产品中.

Sim*_*low 219

好的,所以看了@gawi指出的一些node.js演示文稿,我可以多说一下Haskell与node.js的比较.在演示中,Ryan描述了Green Threads的一些好处,但接着说他并没有发现缺少线程抽象是一个缺点.我不同意他的立场,特别是在Haskell的上下文中:我认为线程提供的抽象对于使服务器代码更容易正确,更加健壮至关重要.特别是:

  • 每个连接使用一个线程允许您编写表示与单个客户端通信的代码,而不是编写同时处理所有客户端的代码.可以这样想:处理具有线程的多个客户端的服务器看起来几乎与处理单个客户端的客户端相同; 主要区别在于fork前者的某处.如果您正在实现的协议非常复杂,那么同时管理多个客户端的状态机会非常棘手,而线程则只允许您与单个客户端进行通信编写脚本.代码更容易正确,更易于理解和维护.

  • 单个OS线程上的回调是协作式多任务处理,而不是抢占式多任务处理,这是线程所能获得的.合作多任务的主要缺点是程序员负责确保没有饥饿.它失去了模块性:在一个地方犯了一个错误,它可以搞砸整个系统.这真的是你不想担心的事情,而抢占是一个简单的解决方案.而且,回调之间的通信是不可能的(它会死锁).

  • 并发性在Haskell中并不难,因为大多数代码都是纯粹的,因此通过构造是线程安全的.有简单的通信原语.在Haskell中使用并发性比在具有不受限制的副作用的语言中更难以拍摄自己.

  • 好的,所以我得到了node.js解决了2个问题:1-并发在大多数语言中很难,2-使用OS线程是扩展的.Node.js解决方案是使用基于事件的并发(w/libev)来避免线程之间的通信并避免OS线程的可伸缩性问题.由于纯度,Haskell没有问题#1.对于#2,Haskell具有轻量级线程+事件管理器,最近在GHC中针对大规模上下文进行了优化.此外,使用Javascript不能被视为任何Haskell开发人员的加分.对于使用Snap Framework的一些人来说,Node.js"只是糟糕". (42认同)
  • 这不是一个单独的问题.如果这个问题是在Haskell中正确搜索工作的最佳工具,或检查Haskell中是否存在优秀的工作工具,则需要对多线程编程不适合的隐含假设提出质疑,因为Haskell确实如此.正如唐·斯图尔特所指出的那样,线索相反.解释为什么Haskell社区也不嫉妒Node.js的答案非常关注这个问题.加维的回答表明这是他的问题的恰当答案. (12认同)
  • 是的!在GHC 7中全新的I/O多路复用使得在Haskell中编写服务器变得更好. (10认同)
  • 请求处理大多数情况下是一系列相互依赖的操作.我倾向于同意为每个阻塞操作使用回调可能很麻烦.线程比回调更适合这个. (4认同)
  • 你的第一点对我来说没有多大意义(作为局外人)......当在node.js中处理请求时,你的回调处理单个客户端.管理状态只会在扩展到多个进程时变得令人担心,即使这样,使用可用库也很容易. (3认同)

Don*_*art 154

Haskell能否提供Node.js的一些好处,即一个干净的解决方案,以避免在不使用多线程编程的情况下阻止I/O?

是的,实际上事件和线程在Haskell中是统一的.

  • 您可以使用显式轻量级线程进行编程(例如,在一台笔记本电脑上使用数百万个线
  • 要么; 您可以基于可伸缩事件通知以异步事件驱动的方式进行编程.

线程实际上是根据事件实现的,并且可以跨多个内核运行,具有无缝的线程迁移,具有记录的性能和应用程序.

例如

32个核心上的并发集合nbody

替代文字

在Haskell中,你既有事件又有线程,因为它是引擎盖下的所有事件.

阅读描述实施的文章.

  • 只是在理论上.Haskell"轻量级线程"并不像你想象的那么轻量级.在epoll接口上注册回调要比安排所谓的绿色线程便宜得多,它们当然比OS线程便宜但是它们不是免费的.创建100.000个使用约.350 MB的内存,需要一些时间.尝试与node.js的100.000连接.没问题.如果没有更快,因为ghc在引擎盖下使用epoll,因此它们不能比直接使用epoll更快.但是,使用线程接口编程非常好. (4认同)
  • 另外:新的IO管理器(ghc)使用具有(m log n)复杂度的调度算法(其中m是可运行线程的数量,n是线程的总数).Epoll具有复杂度k(k是可读/可写fd的数量=.因此ghc在所有复杂度上具有O(k*m log n),如果您面临高流量连接,则不是很好.Node.js只具有线性复杂性通过epoll.让我们不谈论Windows性能...... Node.js更快,因为它使用IOCP. (3认同)
  • 谢谢.我需要消化这一切......这似乎是针对GHC的.我想没关系.Haskell语言有时候是GHC可以编译的任何东西.以类似的方式,Haskell"平台"或多或少是GHC运行时. (2认同)

dan*_*rth 18

首先,我不认为node.js正在做正确的事情暴露所有这些回调.你最终用CPS编写程序(继续传递样式),我认为应该是编译器完成转换的工作.

事件:没有线程操作,程序员只提供回调(如在Snap框架中)

因此,考虑到这一点,如果您愿意,可以使用异步样式编写,但这样做会错过以高效的同步样式编写,每个请求只有一个线程.Haskell在同步代码中非常有效,特别是与其他语言相比时.这是下面的所有事件.

保证回调在单个线程中运行:不存在竞争条件.

你仍然可以在node.js中遇到竞争条件,但这更难.

每个请求都在它自己的线程中.当你编写必须与其他线程通信的代码时,由于haskell的并发原语,使其成为线程安全非常简单.

简单易用的UNIX友好API.好处:优秀的HTTP支持.DNS也可用.

看看hackage,看看自己.

默认情况下,每个I/O都是异步的(但有时候这可能很烦人).这样可以更容易地避免锁定.但是,回调中过多的CPU处理会影响其他连接(在这种情况下,任务应分成较小的子任务并重新调度).

你没有这样的问题,ghc将在真实的OS线程中分配你的工作.

客户端和服务器端的语言相同.(但是我没有看到这个值太多.JQuery和Node.js共享事件编程模型,但其余部分非常不同.我只是看不出服务器端和客户端之间的共享代码如何在实践中很有用.)

哈斯克尔不可能在这里赢得...对吧?再想一想,http://www.haskell.org/haskellwiki/Haskell_in_web_browser.

所有这些都包装在一个产品中.

下载ghc,点燃cabal.每个需要都有一个包.


vlp*_*ans 8

我个人认为Node.js和编程回调是不必要的低级别和有点不自然的事情.为什么在良好的运行时(例如在GHC中找到的运行时)可以为您处理回调并且非常有效地执行回调时编程回调?

与此同时,GHC运行时有了很大的改进:它现在有一个名为MIO的"新的新IO管理器" ,其中"M"代表我相信的多核.它建立在现有IO管理器的基础之上,其主要目标是克服4+核心性能下降的原因.本文提供的性能数字令人印象深刻.看看自己:

借助Mio,Haskell中的真实HTTP服务器可扩展至20个CPU核心,与使用以前版本的GHC的相同服务器相比,可实现高达6.5倍的峰值性能.Haskell服务器的延迟也得到了改善:在中等负载下,与之前版本的GHC相比,预期响应时间缩短了5.7倍

和:

我们还表明,使用Mio,McNettle(一个用Haskell编写的SDN控制器)可以有效扩展到40多个内核,在一台机器上达到每秒超​​过2000万个新请求的吞吐量,因此成为所有现有SDN控制器中最快的.

Mio已经进入GHC 7.8.1版本.我个人认为这是Haskell性能的重大进步.比较以前的GHC版本和7.8.1编译的现有Web应用程序性能将非常有趣.


Gre*_*ber 5

这个问题非常荒谬,因为1)Haskell已经以更好的方式解决了这个问题,并且2)以与Erlang大致相同的方式解决了这个问题.以下是针对节点的基准:http://www.yesodweb.com/blog/2011/03/prelimin-warp-cross-language-benchmarks

为Haskell提供4个内核,它可以在单个应用程序中每秒执行100k(简单)请求.节点不能做多少,也无法跨核心扩展单个应用程序.而且你不需要做任何事情来收获它,因为Haskell运行时是非阻塞的.Erlang中唯一一种内置于运行时的非阻塞IO的其他(相对常见)语言.

  • 荒谬?问题不是"Haskell是否有响应",而是"Haskell响应是什么".当问题被问到时,GHC 7甚至没有被释放,所以Haskell还没有"在游戏中"(除了可能使用像Snap这样的libev的框架).除此之外,我同意. (14认同)
  • 使用4个以上内核的Haskell会降低应用程序的性能.有一篇关于这个问题的论文,它正在积极研究,但它仍然是一个问题.因此,在16核心服务器上运行16个Node.js实例最有可能比使用+ RTS -N16的单个ghc应用程序好得多,因为这个运行时错误确实会慢于+ RTS -N1.这是因为他们只使用一个IOManager,当与许多操作系统线程一起使用时会减慢速度.我希望他们能解决这个问题,但它一直存在,所以我不会抱太大希望...... (2认同)

ago*_*ona 5

恕我直言的事件是好的,但通过回调编程不是.

大多数使Web应用程序编码和调试变得特殊的问题都来自使它们具有可扩展性和灵活性的问题.最重要的是HTTP的无状态特性.这增强了可导航性,但这会导致控制反转,其中IO元素(在本例中为Web服务器)调用应用程序代码中的不同处理程序.这个事件模型 - 或回调模型,更准确地说 - 是一场噩梦,因为回调不共享变量范围,并且导航的直观视图丢失.当用户来回导航以及其他问题时,很难防止所有可能的状态改变.

可以说问题类似于GUI编程,其中事件模型工作正常,但GUI没有导航,也没有后退按钮.这使Web应用程序中可能的状态转换倍增.尝试解决这些问题的结果是重型框架,配置复杂,有大量普遍的魔术标识符,而不会质疑问题的根源:回调模型及其固有的缺乏共享变量范围,没有排序,所以序列必须通过链接标识符来构建.

有基于顺序的框架,如ocsigen(ocaml)海边(smalltalk)WASH(已停产,Haskell)和mflow(Haskell),它们在保持导航性和REST-fulness的同时解决了状态管理问题.在这些框架内,程序员可以将导航表达为命令序列,其中程序发送页面并在单个线程中等待响应,变量在范围内,后退按钮自动工作.这固有地产生更短,更安全,更易读的代码,其中导航对于程序员来说是清晰可见的.(公平警告:我是mflow的开发者)