机架并发 – rack.multithread,async.callback,或两者?

我试图完全理解Rack中并发请求处理的选项。 我已经使用async_sinatra构build了一个长轮询应用程序,现在正在使用throw :async和/或Thin's -threaded标志尝试裸机。 我对这个主题感到满意,但有些东西我无法理解。 (不,我没有错在并行性的并行性,是的,我明白GIL的限制)。

Q1。 我的testing表明, thin --threaded (即rack.multithread=true )在单独的线程(我假设使用EM)同时运行请求,这意味着长时间运行的请求A不会阻止请求B(IO旁)。 这意味着我的应用程序不需要任何特殊的编码(例如callback)来实现并发(同样,忽略阻塞DB调用,IO等)。 这是我相信我所观察到的 – 这是正确的吗?

Q2。 还有另外一个讨论实现并发的方法,涉及到EventMachine.deferthrow :async 。 严格来说,请求不是使用线程处理的。 他们被串行处理,但是将他们的繁重工作和callback传递给EventMachine,后者使用async.callback在以后发送响应。 在请求A已经将其工作卸载到EM.defer之后,请求B开始。 它是否正确?

Q3。 假设上面的或多或less是正确的, 那么一种方法比另一种更有优势吗? 显然 – --threaded看起来像一个神奇的子弹。 有什么缺点吗? 如果没有,为什么每个人都在谈论async_sinatra / throw :async / async.callback ? 也许前者是“我想让我的Rails应用程序在重负载下稍微快一点”,而后者更适合具有许多长时间运行请求的应用程序? 或者也许规模是一个因素? 只是在这里猜测。

我在MRI Ruby 1.9.2上运行Thin 1.2.11。 (仅供参考,我必须使用--no-epoll标志,因为在EventMachine中使用epoll和Ruby 1.9.2有一个长久的,被认为已经解决但并非真正的问题 。洞察力是受欢迎的。)

注意:我使用Thin作为实现asynchronousRack扩展的所有Web服务器(即Rainbows!,Ebb,未来版本的Puma,…)的同义词。

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

Q2。 结合EM.defer使用EM.defer实际上并没有太多意义,因为它基本上也会使用线程池,最终得到类似Q1的结构。 使用async.callback只有在IO中使用eventmachine库时才有意义。 一旦使用正常的Rack响应作为参数调用env['async.callback'] Thin会将响应发送给客户端。

如果主体是一个EM::Deferrable ,则Thin将不会closures连接,直到可延迟成功。 一个相当保守的秘密:如果你不仅需要长时间轮询(即在发送部分响应之后保持连接打开),还可以直接返回一个EM::Deferrable作为body对象,而不必使用throw :async或status代码为-1

Q3。 你猜对了。 螺纹服务可能会改善原本不变的Rack应用程序的负载。 使用Ruby 1.9.3,我的机器上的简单Sinatra应用程序的性能提高了20%,在使用Rubinius或JRuby时可以使用所有内核。 第二种方法是有用的,如果你写你的应用程序在一个平坦的方式。

你可以在Rack之上投入大量的魔法和黑客,让一个非平凡的应用程序利用这些机制(见em-synchrony或sinatra-synchrony),但是这会让你处于debugging和依赖的地狱。

asynchronous的方法是真正意义上的应用程序,往往是最好的解决方法,如networking聊天 。 不过,我不build议使用线程方法来实现长轮询,因为每个轮询连接都会阻塞一个线程。 这将使您无法处理大量的线程或连接。 EM的线程池的默认大小为20个线程,将每个进程限制为20个等待连接。

您可以使用为每个传入连接创build一个新线程的服务器,但创build线程很昂贵(MacRuby除外,但不会在任何生产应用程序中使用MacRuby)。 例子是serv和net-http-server 。 理想情况下,你想要的是请求和线程的n:m映射。 但是那里没有提供服务器。

如果你想了解更多关于这个话题的话题,我在“落基山ruby”(以及其他一些会议)上做了介绍。 录像可以在confreaks上find 。