26

谁能解释每种并发方法的瓶颈是什么?

Unicorn(基于进程)和Puma(基于线程)这样的服务器。

每种方法是否更喜欢 CPU 内核?线程?或者只是时钟速度?还是特殊的组合?

在使用专用服务器的情况下,如何确定所需的最佳 CPU 特性?

以及如何确定 Unicorn 的最佳工人数量,或 Puma 的线程数量?

4

2 回答 2

60

Unicorn 是基于进程的,这意味着每个 ruby​​ 实例都必须存在于自己的进程中。对于每个进程,这可能在 500mb 左右,这将很快耗尽系统资源。Puma 是基于线程的,理论上不会使用相同数量的内存来获得相同数量的并发性。

Unicorn是运行多个进程的,不同进程之间将具有并行性。这受到您的 CPU 内核的限制(更多内核可以同时运行更多进程),但内核将在活动进程之间切换,因此可以运行超过 4 或 8 个进程(无论您拥有多少内核)。您将受到机器内存的限制。直到最近,ruby 对写时复制并不友好,这意味着每个进程都有自己的继承内存(独角兽是一个预分叉服务器)。Ruby 2.0 对写时复制友好,这可能意味着 unicorn 实际上不必将所有子进程加载到内存中。我不是 100% 清楚这一点。阅读有关写入时复制的内容,并查看 jessie storimer 的精彩书籍“使用 unix 进程工作”。我很确定他把它盖在那里。

Puma 是一个线程服务器。MRI Ruby,由于全局解释器锁 (GIL),只能运行单个 CPU 绑定任务一次(参见 ruby​​ 小吃第 127 集,平行谎言)。它将在线程之间进行上下文切换,但只要它是一个 CPU 绑定任务(例如数据处理),它就只会运行一个执行线程。如果您使用不同的 Ruby 实现(例如 JRuby 或 Rubinius)运行服务器,这会变得很有趣。他们没有 GIL,可以并行处理大量信息。JRuby 非常快,而 Rubinius 与 MRI 相比速度较慢,但​​多线程 Rubinius 处理数据的速度比 MRI 快。然而,在非阻塞 IO 期间(例如写入数据库、发出 Web 请求),MRI 将上下文切换到非执行线程并在那里工作,然后在返回信息时切换回前一个线程。

对于Unicorn,我想说瓶颈是内存和时钟速度。对于Puma,我想说瓶颈是您选择的解释器(MRI vs Rubinius 或 JRuby)以及您的服务器正在执行的工作类型(大量 cpu 绑定任务与非阻塞 IO)。

这场辩论有大量的资源。查看 Jessie Storimer 关于这些主题的书籍,使用 ruby​​ 线程使用 unix 进程阅读ryan tomayko 对预分叉服务器的快速摘要,并在谷歌周围搜索更多信息。

在您的情况下,我不知道 Unicorn 或 Puma 的最佳工人数量是多少。最好的办法是运行性能测试并做适合您的事情。没有一种尺寸适合所有人。(虽然我认为 puma 标准是使用 16 个线程池并将其锁定)

于 2013-09-02T14:38:13.307 回答
5

Puma 实际上是多线程和多进程的。您可以在“集群模式”中调用它,它会产生多个分叉的工作人员,这些工作人员将在 MRI 的不同核心上运行。由于 Puma 是多线程的,它可能适合运行与服务器上的内核数相等的进程数。所以对于一个 4 核服务器来说,这样的东西是合适的:

puma -t 8:32 -w 4 --preload

这将处理最多 32 个并发线程,最多同时在 CPU 上运行 4 个线程,并且应该能够最大化服务器上​​的 CPU 资源。该--preload参数预加载应用程序并利用 ruby​​ 2.0 COW 对垃圾收集的改进来减少 RAM 使用。

如果您的应用程序花费大量时间等待其他服务(搜索服务、数据库等),那么这将是一个很大的改进。当一个线程阻塞时,同一进程中的另一个线程可以抢占 CPU 并开始工作。在此示例中,您最多可以并行支持 32 个请求,而仅在 RAM 中运行 4 个进程。

使用 Unicorn,您将不得不分叉 32 个工作人员,这将承受在 RAM 中运行 32 个进程的影响,这是非常浪费的。

如果您的应用程序所做的只是 CPU 运算,那么这将非常低效,您应该减少独角兽的数量,而 Puma 相对于独角兽的优势将会降低。但在 Unicorn 案例中,您必须对您的应用程序进行基准测试并找出正确的数字。Puma 将倾向于通过产生更多线程来优化自身,它的性能范围应该从不比 Unicorn 差(在纯 CPU 情况下)到比 Unicorn 好得多(在应用程序睡眠很多的情况下)。

当然,如果您使用 Rubinius 或 JRuby,那么就没有什么可竞争的了,您可以生成一个运行多核并处理所有 32 个线程的进程。

TL;DR 是我不认为 Unicorn 比 Puma 有太多优势,因为 Puma 实际上使用了这两种模型。

当然,我对 Puma vs Unicorn 在现实世界中运行生产软件的可靠性一无所知。需要关注的一件事是,如果您在一个线程中随意修改任何全局状态,它可能会影响同时执行的其他请求,这可能会产生不确定的结果。由于 Unicorn 不使用线程,因此不存在并发问题。我希望此时 Puma 和 Rails 在并发问题方面都已经成熟,并且 Puma 可以在生产中使用。但是,我不一定希望我在 GitHub 上找到的每个 rails 插件和 ruby​​gem 都是线程安全的,并且希望必须做一些额外的工作。但是一旦你成功地在第三方库中发现线程问题,你可能已经足够大了,你可以' 无法负担运行这么多 Unicorn 进程的 RAM 成本。OTOH,我了解并发错误并且我对 Ruby 很熟悉,因此调试成本对我来说可能比在云中购买 RAM 的成本要低得多。YMMV。

另请注意,我不确定在估计传递给“-w”的值时是否应该计算超线程内核或物理内核,并且您需要自己进行性能测试,以及性能测试要用于 -t 的值。尽管即使你运行两倍于你“需要”的进程数量,内核中的进程调度程序也应该能够毫无问题地处理这个问题,直到你的 CPU 饱和,在这种情况下,无论如何你都会遇到更大的问题。我可能会建议为每个超线程内核启动一个进程(在 MRI 上)。

于 2015-12-13T21:47:50.353 回答