Ruby 从 1.8 => 1.9 => 2.0 => 2.1 一直在 GC 上不断地改进,最近 2.1.1 刚刚发布,抽空将我们的应用从 2.0 升级了一下,记录一下相关改动。
我们在服务器使用的是 rvm,首先更新一下 rvm:
rvm get head
淘宝提供了 ruby 相关的镜像,可以更新一下源,后续安装会快很多:
sed -i 's!cache.ruby-lang.org/pub/ruby!ruby.taobao.org/mirrors/ruby!' $rvm_path/config/db
安装 2.1.1:
rvm install 2.1.1
建议使用 railsexpress 的性能优化补丁(其中包括已经合并到 ruby 2.2 method cache 的改进):
rvm install 2.1.1 --patch railsexpress
设置 2.1.1 为默认版本:
rvm use 2.1.1 --default
OobGC 优化: 我们之前使用的 unicorn 自带的 OobGC,它会固定在每 N 次请求后,执行一次 GC。tmm1 写了另一个 OobGC: https://github.com/tmm1/gctools ,它利用 ruby 2.1 新的 gc 事件,使用 c 扩展来进行更智能的 OobGC,配置很简单,在 config.ru 里面加上:
require 'gctools/oobgc'
use GC::OOB::UnicornMiddleware
不过他不支持请求阶段的 GC.disable,实际用我们的应用测下来和 unicorn 自带的 OobGC+GC.disable 相比,在普通压力测试下,平均响应时间稍慢 2~3%,服务器的 cpu 消耗会少 2% 左右。
除了 OobGC 外,这个 gem 还提供了 GC 的日志输出,只要加上:
require 'gctools/logger'
可以在 stderr 看到具体的 GC 执行情况,用来判断 GC 参数调整是否合理十分有用。
GC 参数调整: 和 Ruby 2.0 相比,2.1 多了一些分代 GC 的参数,这篇文章 http://tmm1.net/ruby21-rgengc/ 非常详细地介绍了各个参数的意义,还提供了 github 用的参数配置。配合 gctools/logger,我们最终调整的 GC 参数如下:
export RUBY_GC_HEAP_INIT_SLOTS=500000
export RUBY_GC_HEAP_FREE_SLOTS=700000
export RUBY_GC_HEAP_GROWTH_FACTOR=1.25
export RUBY_GC_HEAP_GROWTH_MAX_SLOTS=300000
export RUBY_GC_MALLOC_LIMIT=80000000
export RUBY_GC_OLDMALLOC_LIMIT=80000000