部署 Ruby 的多线程应用服务器介绍

robbin · 发布于 2013年05月08日 · 最后由 wangping 回复于 2015年04月24日 · 13706 次阅读
4087
本帖已被设为精华帖!

随着Rails4.0的发布,Ruby的Web开发社区开始进入多线程的时代了:

  1. 从Ruby1.9开始,多线程已经是native thread了,尽管有GIL全局锁的存在,但是对于IO并发来说,已经可以实现并行处理了。
  2. Rails4.0开始,默认打开了多线程运行模式,将推动整个Ruby的Web开发社区迁移到多线程Web模式。

多线程服务器的模型,相比传统多进程服务器模型,可以非常有效的提高IO并发的吞吐量。我现在开发Web应用,已经全部改用多线程了,我写过一个相关的文档介绍:Web并发模型粗浅探讨,有哪些Ruby的应用服务器可以良好的支持多线程呢?

Rainbows

rainbowsunicorn是同一个作者Eric Wong开发的,rainbows和unicorn非常像,他们之间的主要区别就是:unicorn是多进程服务器,rainbows是多线程服务器,此外基本用法,配置都一样的,本身rainbows就是基于unicorn开发的。

unicorn现在被广泛的使用在很多负载非常高的生产环境中,rainbows也和unicorn同样非常稳定。由于Ruby1.9的GIL,多线程并发只能有效使用1颗CPU内核,因此在多核服务器上,需要运行多个rainbows进程。一般来说,服务器有多少CPU内核,就启动多少个rainbows worker进程。每个rainbows进程里面再启动多个线程,因此理想情况下,能够同时处理的IO并发请求数量等于 “进程数” × “线程数”,看一个例子:

rainbows.rb,注意以下3项配置:

use :ThreadPool # 使用线程池模式,进程启动的时候创建好线程数 worker_processes 4 # 创建多少个进程 worker_connections 64 # 每个进程创建多少个线程

以上面的配置为例,如果你在一台4核服务器上面部署应用,希望尽量使用服务器资源,那么可以创建4个进程,每个进程创建64个线程,因此整个服务器IO并发处理能力是256个执行线程,这比传统的多进程模式要高得多。

控制rainbows服务器的shell脚本很容易写:rainbows.sh,rainbows和unicorn一样,都是通过给进程发送信号来控制服务器的。

以上的配置启动以后,会有5个rainbows进程:1个master控制进程,4个worker工作进程。master进程负责加载应用程序代码,创建和销毁worker进程,分发请求;worker进程负责处理请求。这个master-workers的工作模式和nginx是一样的。你还可以通过给master进程发送信号,让master进程创建更多worker进程,或者减少worker进程,还可以实现“平滑的重启”,即在不中断web请求服务的同时,重新启动进程,加载新的应用代码,这一点通过: rainbowsctl reload就可以实现。

此外如果你使用的是Ruby2.0,还可以在rainbows.rb里面打开 copy on write 特性,如下:

preload_app true GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true

可以让多个进程共享加载应用程序框架和库的内存空间,节省很多物理内存。

总之对于大型多线程Ruby Web应用,推荐使用rainbows,我使用下来感觉也很不错。

Puma

我在相当长一段时间都不怎么关注puma,因为puma是个单进程多线程服务器,没有cluster模式,不太适合真实有负载的环境,对于多核服务器,启动多进程跑cluster是必须的。但是最近Puma升级到2.0版本以后,也支持了多进程cluster模式,而且也是master-workers的方式,类似nginx和rainbows,请看配置文件:puma.rb

workers 0 # 指定创建多少个worker进程,如果0则不打开cluster模式 threads 0, 16 # 最少线程和最多线程,可根据请求量在这个范围自动伸缩

通过workers设置进程数,通过threads设置线程数,最大IO并发请求量等于进程数乘以最大线程数。Puma比较有意思的地方在于:

  1. 通过workers设置,可以在单进程和多进程cluster模式之间自动切换,如果是0,则不启动master进程,只有一个单进程,如果workers不是0,则启动1个master和多个workers。而且master进程不会加载应用代码,master进程占有内存非常少,当然这个的代价就是如果启动workers进程很多,启动时间会很长。
  2. 线程可以根据请求量自动在min和max之间伸缩,对于较少的请求,启动的线程数少,可以节省资源。

此外puma也支持“平滑的重启”,也提供了很多有趣的状态控制方式,请看puma.sh Puma启动的时候可以设置一个状态文件,它会把进程的运行状态写进去,方便你控制进程的运行。

Rainbows or Puma

我对Rainbows和Puma做了一些简单的性能压力测试,测试结果表明两者之间的性能差异非常小,Rainbows稍稍胜出。使用哪个,更多取决于个人偏好。我觉得大型应用使用Rainbows更好一些,希望节省服务器资源的小型应用更适合用Puma。这是因为:

  1. Rainbows基于unicorn的代码,稳定性得到了大量高负载应用的考验。
  2. Rainbows提供了很多通过信号控制进程的手段,方便系统管理员干预应用服务器运行。
  3. 在单进程模式下Puma只有1个工作进程,单Rainbows仍然会启动master-worker模式,因此Puma的单进程更节省内存。
  4. Puma的线程可以根据请求自动伸缩,对于请求量小的应用,比较节省资源。

Zbatery

zbatery也是unicorn和rainbows的作者Eric Wong开发的,配置文件可以直接用rainbows的,它是rainbows的简化版本,只支持单进程多线程,没有master进程,信号支持的也不完整,而且很久没有升级过了。比较正规的生产环境可能就不太适合了。但zbatery的好处是特别节省内存,比单进程的puma还要节省很多内存,如果你想在一个VPS上跑很多web应用,每个web应用流量都很小,那用zbatery到非常合适。

共收到 49 条回复
3407e5

前段时间刚好用了rainbows,关注中!谢谢robbin的讲解。

4584

@robbin 那和Passenger比如何?我就觉得用Passenger要编译比较麻烦……性能方面,没有做过对比。。。

4087

#2楼 @imlcl Passenger性能不如unicorn/rainbows,这个我测试过,而且差的还挺多。Passenger最大的毛病是和nginx绑定的太紧密,每次升级passenger,要连带升级nginx,迁移很多nginx的配置,非常痛苦。

4584

#3楼 @robbin 是,你有没有试4.0.0的?已经release了,听说性能和稳定性都改进了。我用这东西是因为刚学Rails时,觉得这东西配起来比较方便一点。但后续就和你所说,麻烦,每次都得编译成模块加载。看来我也试试布署rainbow或puma吧

2880

这些服务器都使用了 Zed Shaw 的 mongrel 遗留下来的用 ragel 生成的基于状态机的 html header parser...

1107

#5楼 @luikore 这意味这什么呢?

6067

@robbin不知道和thin比起来怎么样,一直在用thin。

332

说明 Zed Shaw 的代码很经典,孵育了多个很棒的应用服务器,他离开 Ruby 届真是一大损失。

2622

学习了。。

C5fc5e

#7楼 @zlfera 比thin肯定要好, thin只是基于EventMachine做了个事件驱动, EM的并发性能你懂的,Puma是基于多线程的ReActor的机制 , 类似Java的NIO机制

De6df3

#7楼 @zlfera Thin 基于 EventMechine ,有特别的用处。 Thin 是单线程多进程的方式,并且没有 master 进程

4584

#11楼 @huacnlee @zlfera 我development时就用thin,好像ruby-china也是吧

1249

@robbin 如果rails跑多线程,大家都知道的,多线程共享数据区,所以对我们写的ruby代码有没有什么地方需要特别注意的,特别是rails里面。

1249

@robbin rainbows 的shell脚本 rainbowsctl 无法访问(404)。能否贴哈脚本代码。

4087

#15楼 @outman 改了个文件名,现在可以了。

4087

#13楼 @outman 没什么特别需要注意的,database.yml里面connection pool的数字要和你线程池的数字保持一致,另外避免在内存当中维持全局可写的变量。

96

#6楼 @jasl 这意味着你该紧跟Zed Shaw的脚步...

1107

#18楼 @bhuztez 好吧。。。。

4

#6楼 @jasl 意味着 Zed Shaw 有多么牛逼....

4

37signal 在不少地发使用了 rainbows... 而且效果不错,至少稳定性是得到了验证的...

987b5d

Puma发展很迅速(这点很可观),我也用在一些比较小的项目中了。

59

最近刚把自己的项目跑在Puma上了,好文章收藏了

5610

额。学习了,现阶段还是development模式。。连redmine都是。。。

96

大家有必要了解一下node.js的,1, 天生non-blocking io, 性能没法说, cluster是标准库提供的; 2, 社区逐渐丰满,几乎ruby的好东西都被复制到node上了。3, 前后端通吃

96

一直用thin,不喜欢unicron的配置方法,感觉thin配置起来更符合我习惯,而且在考虑用eventmachine。

C5fc5e

如果不用Rubinius或JRuby, 不建议使用puma, 感觉unicron是最稳妥的方案

2880

#25楼 @kevinxu 哈哈, 不支持也不可能支持多线程模式恰恰是 node.js 的致命伤之一

non-blocking server / app 在 ruby 也有很多, 性能和 node 区别不大. 而且 ruby 有 Fiber 支持不需要像 node.js 那样写 callback 套 callback (不过现在 node.js 的写法有改进了). 不过就和这个话题没什么关系了.

2880

提到 Zed Shaw 是想起

多进程模式部署的话 (现在性能和内存表现均最佳的是 eventmachine + 多进程), 服务器间多进程共享数据的最佳方案是使用 Zed Shaw 推荐的 0mq 进行通信...

mongrel2 就是去掉了多线程模式

96

一直用thin比较多,现在看来要尝试一下rainbows. Rails一直很少强调多线程,线程安全,Rails4不知道会不会遇到什么问题。

F7b32c

谢谢robin的分享 之前用passenger 后来服务器升级的时候换了unicorn 前几周新装redmine试了一下puma,结果puma莫名地中止了几次 简单查了一下,好像原因是rails调用了系统命令,但某些命令不存在,rails抛了exception,puma进程可能跟着退出了。 现在换了unicorn没发生这种现象。

等闲的时候再仔细研究一下puma退出原因。

96

#28楼 @luikore callback套callback到不是问题; 我一般做法是1, 用coffee, 2,好的模块划分, 3 async等类似的库

nodejs算是单进程多线程(libuv用多线程+non-block io做异步), 我说的cluster是在进程级别更好的利用多核的cpu资源

至于孰好孰坏, 难分伯仲, 各有各的应用场景吧 - 我个人还是ruby fans :)

96

楼主发贴,必属精品

96

先不急看正文,正在看楼主开头给出的PPT。PPT看到一半已经收益良多了,忍不住来回帖表达下感谢。

96

跑一下题,@robbin 我在csdn会员还有半年的剩余期限的情况下又充了一年的值,可是新充的覆盖了以前的,换句话说我之前那半年的就没了,这个问题谁能搞定?

5106

ruby 性能变更强啦。

2697

#36楼 @xlgwr 强的不明显

2697

#27楼 @SharpX unicron比thin有什么优点呢?

1411

希望ruby app server越来越强啊,要不ruby大面积推广有困难啊

96

#30楼 @jimrokliu 怎么感觉头像像是昨天的我们来约会吧的男嘉宾

694

最近在用puma,感觉挺爽的,不过好像没有unicorn的zero downtime,还是我没发现?

4744

@robbin 问个puma的内存消耗问题 机器是4核,8G,ubuntu 12.04,puma起的是2个worker,线程是0:16 刚开始,每个worker只有100Mb左右,这才一个星期,内存增长到每个500Mb。这种情况正常吗?

4087

#42楼 @iamroody 你换成rainbows比对一下,如果rainbows每个worker也增长到500mb,那就是你程序内存泄露了,如果rainbows内存不高,说明puma有问题。

我自己对比观察下来,确实发现puma长期运行,内存占用似乎比rainbows高一些。

4744

#43楼 @robbin 多谢,准备测试一下!除了这种方法以外,还有其他更方便的方法来检测rails程序的内存泄露吗?

4744

#43楼 @robbin 看到你之前的一篇文章《监视Rails进程内存泄漏的技巧》,正在学习中 http://www.iteye.com/topic/307271

5210

是不是就不用必须启动多个服务了呢

4744

切换到puma,遇到好多不稳定的问题,现在暂时又切回unicorn了,sigh~

5190

@robbin jruby下设置了workers 4,如何能对比出workers 0的区别?

9861

rainbowsctl reload 具体如何实现的呢?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册