将一个项目从
rails2.3+ruby1.8升级到rails3.2+ruby1.9 之后,性能测试发现原先平均500ms的请求,变成了750ms,性能损失达到了50%,经过一些性能优化以后,改善到了400ms左右,记录一下优化的3个地方。
1. GC调整
根据一些资料,Rails3.2的call stack比2.3深了N多层,导致GC会变得更加频繁,所以首先针对GC做profile,对比升级前后的数据:
class BrowsingTest < ActionDispatch::PerformanceTest
self.profile_options = { :runs => 20, :metrics => [:wall_time, :memory, :gc_time, :gc_runs], :output => 'tmp/performance', :formats => [:flat] }
#...
end
rake test:profile
gc_time: 465 ms
gc_runs: 3
而在2.3,20次相同performance profile test,gc_runs是1,这说明了Rails 3.2确实比2.3 GC要频繁,相应的GC参数需要进行调整,这个是调整后的参数(和2.3相比,要加大RUBY_GC_MALLOC_LIMIT):
export RUBY_HEAP_MIN_SLOTS=620000
export RUBY_FREE_MIN=100000
export RUBY_GC_MALLOC_LIMIT=62000000
具体的值根据应用不同,可以进行微调,再跑rake test:profle可以得出适合你应用的合理值,另外,推荐用rvm,这样对不同应用配置不同的参数,上面的参数设置可以在rvm env文件里面配置:
~/.rvm/environments/ruby-1.9.2-xxx
2. Cache调整
在搜索GC资料的时候,看到了一篇关于Memcached client的博客
http://www.mikeperham.com/2010/09/19/dalli-performance-and-garbage-collection/
因为我们的应用使用Memcached操作比较多,平均每个页面有8次左右的Memcached操作,改了一下配置,使用dalli和kgio:
# Gemfile
gem 'dalli', :git => 'https://github.com/mperham/dalli.git'
gem 'kgio'
# config/environments/production.rb
config.cache_store = :dalli_store, 'memcached_server'
3. Rack 中间件调整
在GC调整里面说到的call stack变多的ppt在这里:
http://www.slideshare.net/tenderlove/railsconf-2011-keynote,主要是rake middleware造成,在Rails 3.2项目下运行rake middleware,可以发现和2.3相比,多了N的middleware:
use Rack::Cache
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x8f4f210>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use ActionDispatch::Head
use Rack::ConditionalGet
use Rack::ETag
use ActionDispatch::BestStandardsSupport
run Rails32::Application.routes
其实对于我们的应用来说,很多都是不需要的,比如整页Cache/ETag/X-Request-Id等,我们的应用都是动态请求,所以都是强制无Cache,我列了一下可以去除的和他们的用途:
Rack::Cache 整页缓存
Rack::Runtime 记录X-Runtime(方便客户端查看执行时间)
ActionDispatch::RequestId 记录X-Request-Id(方便客户端查看请求具体在集群中的哪台执行)
ActionDispatch::RemoteIp 防止IP伪造(可以在web server上做)
ActionDispatch::Callbacks 设置callback
Rack::ConditionalGet 设置If-None-Match and If-Modified-Since
Rack::ETag 设置ETag
ActionDispatch::BestStandardsSupport 设置X-UA-Compatiblecd(可以在web server上做)
写一个initializer,去除掉这些不必要的中间件。
整个优化的重点就是减少GC,希望Rails 4能够更加精简,希望Ruby 2.0的GC能够有更好的性能。