有人能解释一下每种并发方法的瓶颈在哪里吗?
像Unicorn(基于进程)和Puma(基于线程)这样的服务器。
每种方法喜欢CPU核心吗?线程?还是仅仅是时钟速度?或者是特定的组合?
如何确定在使用专用服务器的情况下需要的最佳CPU特性?
而在使用Unicorn的情况下,如何确定最佳的worker数量,或者在使用Puma的情况下,如何确定最佳的线程数量?
有人能解释一下每种并发方法的瓶颈在哪里吗?
像Unicorn(基于进程)和Puma(基于线程)这样的服务器。
每种方法喜欢CPU核心吗?线程?还是仅仅是时钟速度?或者是特定的组合?
如何确定在使用专用服务器的情况下需要的最佳CPU特性?
而在使用Unicorn的情况下,如何确定最佳的worker数量,或者在使用Puma的情况下,如何确定最佳的线程数量?
Unicorn是基于进程的,这意味着每个Ruby实例都需要存在在自己的进程中。每个进程可能需要500MB左右的空间,这将迅速耗尽系统资源。而Puma是基于线程的,理论上不会使用同样数量的内存来达到相同的并发量。
因为Unicorn运行多个进程,所以不同进程之间会有并行性。这受限于你的CPU核心数(更多核心可以同时运行更多进程),但内核会在活动进程之间切换,因此可以运行超过4或8个进程(无论你有多少个核心)。 你的机器内存将限制你的进程数。 直到最近,Ruby不支持写时复制,这意味着每个进程都有自己继承的内存(Unicorn是一个preforking服务器)。Ruby 2.0支持了写时复制,这意味着Unicorn实际上不必将所有子进程加载到内存中。我不是100%清楚这一点。 阅读关于写时复制的文章,并查看Jessie Storimer的精彩书籍“使用Unix进程工作”。 我相信他在其中介绍了它。
Puma是一个线程服务器。由于全局解释器锁(GIL),MRI Ruby只能同时运行一个CPU绑定任务(例如,数据处理)(参见Ruby Tapas第127集,parallel fib)。它将在线程之间进行上下文切换,但只要它是一个CPU绑定任务,它只会运行一个执行线程。如果你用不同的Ruby实现(比如JRuby或Rubinius)运行服务器, 这就变得有趣了。它们没有GIL,并且可以并行处理大量信息。JRuby相当快,而虽然Rubinius比MRI慢,但多线程的Rubinius比MRI更快地处理数据。然而,在非阻塞IO期间(例如,写入数据库,进行web请求),MRI将上下文切换到非执行线程并在那里工作,然后在返回信息时切换回先前的线程。
针对 Unicorn,我认为瓶颈在于内存和时钟速度。针对 Puma,我会说瓶颈在于选择的解释器(MRI vs Rubinius 或 JRuby)和服务器正在执行的工作类型(大量CPU绑定任务 vs 非阻塞IO)。关于这个争论,有很多优秀的资源可供参考。请查看 Jessie Storimer 的书籍:《使用Ruby线程》 和 《使用Unix进程》;阅读 ryan tomayko 的快速预分叉服务器摘要,并在谷歌上查找更多信息。 我不知道在您的情况下 Unicorn 或 Puma 的最佳工人数量是多少。最好的方法是运行性能测试并根据实际情况进行调整。没有一种通用的大小。 (尽管我认为 Puma 的标准是使用一个 16 线程池并将其锁定)Puma实际上是多线程和多进程的。您可以在“集群模式”下调用它,它将生成多个分叉工作程序,这些程序将在MRI上的不同核心上运行。由于Puma是多线程的,因此可能适合运行与服务器上核心数量相等的进程数。因此,对于4核服务器,应该使用类似以下内容:
puma -t 8:32 -w 4 --preload
-preload
参数预加载应用程序,并利用Ruby 2.0 COW改进垃圾回收以减少RAM使用。