我试图完全理解Rack中并发请求处理的选项.我已经使用async_sinatra构建了一个长轮询应用程序,现在正在使用throw :async和/或Thin的线程标志对裸机Rack 进行试验.我对这个话题很熟悉,但有些事情我就是搞不懂.(不,我没有把并发误认为并行,是的,我确实理解GIL施加的限制).

问题1.我的测试表明,thin --threaded个(即rack.multithread=true个)在不同的线程中并发运行请求(我假设使用EM),这意味着长时间运行的请求A不会阻止请求B(IO除外).这意味着我的应用程序不需要任何特殊编码(例如回调)来实现并发性(同样,忽略阻止DB调用、IO等).This is what I believe I have observed - is it correct?

问题2.还有另一种更常被讨论的实现并发性的方法,涉及EventMachine.deferthrow :async.严格来说,请求是使用线程处理的.它们是串行处理的,但会将繁重的任务和回调传递给使用async的EventMachine.回调以稍后发送响应.请求A将其工作卸载给EM.DEREF后,请求B开始.Is this correct?

问题3.假设上面的说法或多或少是正确的,那么显然--threaded看起来像一颗神奇的子弹.有什么缺点吗?如果不是,为什么每个人都在谈论async_sinatra/throw :async/async.callback?也许前者是"我想让我的Rails应用程序在重载情况下更快速",而后者更适合于有许多长时间运行请求的应用程序?或者规模是一个因素?只是猜猜而已.

我在Ruby 1.9.2上运行精简版1.2.11.(仅供参考,我必须使用--no-epoll标志,因为EventMachine使用epoll和Ruby 1.9.2时有a long-standing, supposedly-resolved-but-not-really problem个标志.这不重要,但欢迎有任何见解.)

推荐答案

注意:我使用Thin作为实现异步Rack 扩展的所有web服务器的同义词(即Rainbows!、Ebb、Puma的future 版本等等)

Q1.正确.它将响应生成(aka call)封装在EventMachine.defer { ... }中,这将导致EventMachine将其推送到其内置线程池中.

Q2.async.callbackEM.defer结合使用实际上没有太大意义,因为它基本上也会使用线程池,最终得到类似的构造,如Q1所述.当仅将eventmachine库用于IO时,使用async.callback是有意义的.一旦以正常Rack 响应作为参数调用env['async.callback'],Thin将向客户端发送响应.

如果主体是EM::Deferrable,Thin将不会关闭连接,直到延迟成功.一个相当保守的秘密:如果你想要的不仅仅是长轮询(即在发送部分响应后保持连接打开),你也可以直接返回EM::Deferrable作为body对象,而不必使用throw :async-1状态码.

你猜对了.提高线程化应用程序在Rack might上的负载,否则将保持不变.在我使用Ruby 1.9.3的机器上,简单的Sinatra应用程序的性能提高了20%,在Rubinius或JRuby上运行时,性能更高,所有内核都可以利用.如果以事件方式编写应用程序,第二种方法很有用.

为了让一个无事件的应用程序利用这些机制(请参阅em synchrony或sinatra synchrony),您可以在Rack 上施展大量魔法和黑客攻击,但这会让您陷入调试和依赖的地狱.

异步方法在应用程序中非常有意义,这些应用程序往往最好使用事件处理方法(如a web chat)来解决.但是,我不建议使用线程方法来实现长轮询,因 for each 轮询连接都会阻塞一个线程.这将给你留下大量无法处理的线程或连接.默认情况下,EM的线程池大小为20个线程,将每个进程的等待连接限制为20个.

你可以使用一个服务器, for each 传入的连接创建一个新线程,但创建线程是昂贵的(MacRuby除外,但我不会在任何生产应用程序中使用MacRuby).例如servnet-http-server.理想情况下,您需要的是请求和线程的n:m映射.但目前还没有提供这种服务的服务器.

如果你想了解更多关于这个话题的信息:我在洛基山Ruby(以及许多其他会议)上做了一个关于这个的演讲.视频记录可以在on confreaks页找到.

Ruby相关问答推荐

为什么 rdoc 注释似乎以两个井号/井号符号开头?

Python 正则表达式是否等同于 Ruby 的原子分组?

如何从 url 获取文件扩展名?

Ruby:p *1..10中的星号是什么意思

判断文件是否包含字符串

如果尚未定义,则设置 Ruby 变量

查找两个数组之间的共同值

Ruby:module、require和include

使用 for each 时识别最后一个循环

Ruby 设计模式:如何制作可扩展的工厂类?

获取下周一(或一周中的任何一天)的日期的 Ruby 代码

Ruby中block和&block的区别

如何转换 Ruby 哈希,使其所有键都是符号?

安装 Jekyll 时出错 - 本机扩展构建

Ruby:define_method 与 def

Ruby:捕获异常后继续循环

为依赖于环境变量的代码编写规范的最佳方法是什么?

如何在Ruby中将字符串拆分为仅具有给定字符的两部分?

如何从字符串创建 Ruby 日期对象?

如何删除已安装的 ri 和 rdoc?