如何调试Rails资源预编译,这是无法忍受的慢 [英] How to debug a Rails asset precompile which is unbearably slow

查看:162
本文介绍了如何调试Rails资源预编译,这是无法忍受的慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究一个Rails 3.2项目,最近几个月的资产有所增加,尽管我不认为这个项目很大。资产由JS(无咖啡脚本)和SASS文件组成;我们有很多图像,但是自从早期以来,它们已经在那里更少了,所以我不认为它们是一个重要因素。我们可能有大约十几个lib,大多数是小的,最大的是Jquery UI JS。部署是通过Capistrano完成的,开始明显的是部署到分期显着快于生产。为了说明同时避免不同服务器和网络效应的因素,我只需在笔记本电脑上依次运行以下三个命令,如下所示:

  $ time RAILS_ENV =生产包exec rake资产:precompile 
^ Crake aborted!
[注意我中止这个运行,因为我觉得这是愚蠢的长...]
真实52m33.656s
用户50m48.993s
sys 1m42.165s

$ time RAILS_ENV =分期捆绑exec rake资产:precompile
real 0m41.685s
用户0m38.808s
sys 0m2.803s

$ time RAILS_ENV =开发包exec rake资产:precompile
real 0m12.157s
用户0m10.567s
sys 0m1.531s

所以我没有抓我的头。为什么各种环境之间存在如此巨大的差异?我可以理解发展与分期之间的差距,但是我们的分级和生产配置是相同的。 (我应该指出,生产编译将在大约2小时后完成!)



尽管最终结果是让我的预编译更快,我想通过理解来完成这个所有的时间都在进行,为什么Rails环境之间有如此大的差异。我已经看过有关使用不同的压缩器等的其他帖子,但是我找不到有关如何调试这些耙子任务的任何信息来确定花费时间的地方,并确定哪些设置可能导致如此巨大的差异。 p>

我不知道人们可能需要什么额外的信息,所以如果和当评论问,会更新。 TIA



更新:以下提供的其他信息



config / environments / production.rb config / environments / staging.rb (它们完全一样):

  MyRailsApp :: Application.configure do 
#代码没有在请求之间重新加载$ ​​b $ b config.cache_classes = true

#全部错误报告被禁用并缓存已启用
config.consider_all_requests_local = false
config.action_controller.perform_caching = true

#禁用Rails的静态资产服务器或者nginx将会这样做)
config.serve_static_assets = true
config.static_cache_control =public,max-age = 31536000
config.action_controller.asset_host =//#{MyRailsApp: :CONFIG [:cdn]}

#压缩JavaScript和CSS
config.assets.compress = true

#不要回退到资产管道,如果预编译资产错过
config.assets.compile = false

#为资产URL生成摘要
config.assets.digest = true

#启用I18n的区域设置回退(使查找任何地区都可以回到
#I18n.default_locale当翻译无法找到时)
config.i18n.fallbacks = true

#向注册的听众发送弃用通知
config.active_support.deprecation =:通知
结束

基本配置/应用程序。 rb是:

  require File.expand_path('../ boot',__FILE__)

require 'rails / all'

如果定义?(Bundler)
#如果您在部署到生产之前预先编译资产,请使用此行
Bundler.require(* Rails.groups assets => %w(开发测试)))
#如果您希望您的资产在生产中懒惰编译,请使用此行
#Bundler.require(:default,:assets,Rails.env)
end
module MyRailsApp
CONFIG = YAML.load_file(File.join(File.dirname(__ FILE__),'config.yml'))] [Rails.env]

class Application< ; Rails :: Application

#具有要自动加载的类和模块的自定义目录。
config.autoload_paths + =%W(#{config.root} / lib)
config.autoload_paths + =%W(#{config.root} / app / workers)

#配置Ruby 1.9的模板中使用的默认编码。
config.encoding =utf-8

#配置将从日志文件中过滤的敏感参数。
config.filter_parameters + = [:password]

#启用资产管道
config.assets.enabled = true

#停止预编译对于数据库
config.assets.initialize_on_precompile = false

#您的资产版本,如果要过期所有资产,请更改此资源
config.assets.version ='1.0 '

#修复资产管道中的字体
#http://stackoverflow.com/questions/6510006/add-a-new-asset-path-in-rails-3-1
config.assets.paths<<< Rails.root.join('app','assets','fonts')

config.middleware.insert 0,'Rack :: Cache',{
:verbose => true,
:metastore => URI.encode(file:#{Rails.root} / tmp / dragonfly / cache / meta),
:entitystore => URI.encode(file:#{Rails.root} / tmp / dragonfly / cache / body)
}#,除非是Rails.env.production? ##取消注释Rails 3.1,
##中的除非,因为它已经在生产中插入了Rack :: Cache

config.middleware.insert_afterRack :: Cache,Dragonfly: :Middleware',:images

config.action_mailer.default_url_options = {:host => CONFIG [:email] [:host]}
config.action_mailer.asset_host ='http://'+ CONFIG [:email] [:host]
end
end
宝贝文件:

  
gem'rails','3.2.13'
gem'mysql2'
gem'dragonfly','> = 0.9.14'
gem'rack-cache',:require => 'rack / cache'
gem'will_paginate'
gem'dynamic_form'
gem'amazon_product'#查找Amazon ASIN图书代码
gem'geoip'
宝贝'mobile-fu'
gem'airbrake'
gem'newrelic_rpm'
gem'bartt-ssl_requirement','〜> 1.4.0',:require => 'ssl_requirement'
gem'dalli'#memcache for api_cache
gem'api_cache'
gem'daemons'
gem'delayed_job_active_record'
gem'attr_encrypted'
gem'rest-client'
gem'json','> = = 1.7.7'
gem'carrierwave'#简化文件上传
gem'net-scp'

#默认情况下,宝石仅用于资产,而不需要
#在生产环境中。
group:assets do
gem'therubyracer'
gem'sass-rails','〜> 3.2.3'
宝石'罗盘','〜> 0.12.alpha'
gem'uglifier','> = 1.0.3'
gem'jquery-fileupload-rails'
end

gem'jquery- rails'
gem'api_bee',:git => 'git://github.com/ismasan/ApiBee.git',:ref => '3cff959fea5963cf46b3d5730d68927cebcc59a8'
gem'httparty','> = = 0.10.2'
gem'twitter'

#验证提供者
gem'omniauth-facebook'
gem'omniauth-twitter'
gem'omniauth-google-oauth2'
gem'omniauth-identity'
gem'omniauth-readmill'
gem'bcrypt-ruby ',〜> 3.0.0#需要omniauth-identity
gem'mail_view'

#使用ActiveModel has_secure_password
#gem'bcrypt-ruby',' 〜> 3.0.0'

#使用Capistrano部署
组:开发做
gem'capistrano'
gem'capistrano-ext'
gem'capistrano_colors'
gem'rvm-capistrano'

#要求Hoof,Linux相当于Pow
gem'独角兽'
end

组: test,:development do
gem'rspec-rails'
gem'pry'
gem'pry-rails'
end

group:test do
gem'factory_girl_rails'
gem'capybara'
gem'cucumber-rails'
gem'database_cleaner'
gem'launchy'
gem'ruby- debug19'
#相当打印的测试输出
gem'shoulda-matchers'
gem'simplecov',:require => false
gem'email_spec'
gem'show_me_the_cookies'
gem'vcr'
gem'webmock','1.6'
end


解决方案

这可能不会完全回答你的问题,但我相信是一个体面的足够的开始。如您所见,精确的答案将取决于个人应用程序,宝石版本等。



所以。对于与资产相关的工作,如您所知,Rails使用了一个名为Sprockets的库,在较新的Rails版本中,我相信Rails作为Railtie。它初始化一个链轮环境,可以做一些事情,如查看资产清单,加载这些文件,压缩它们,给出已编译的资产明智的名称等。



由默认情况下, Sprockets :: Environment 将其活动记录到 STDERR ,日志级别为致命,这在这些情况下不是很有用。幸运的是, Sprockets :: Environment (作为 2.2.2 )具有可写入的logger属性,您可以修补通过Rails,使用初始化程序。






所以,这里是我建议的,开始:



config / initializers 中,创建一个文件,像 asset_logging.rb 。在其中,将:

  Rails.application.assets.logger = Logger.new($ stdout)

这将覆盖默认记录器,其中将会提供更多信息到 STDOUT 。一旦你设置好了,然后运行你的资产预编译任务:

  rake RAILS_ENV =生产资产:预编译

您应该看到稍微更有趣的输出,例如:


  ... 
编译jquery.ui.core.js(0ms)(pid 66524)
编译的jquery。 ui.widget.js(0ms)(pid 66524)
编译jquery.ui.accordion.js(10ms)(pid 66524)
...
/ pre>




但是,最后的答案将取决于:




  • 你要记录这个资产的东西,你想要走多远?/ b> b $ b
  • 你是什么特定版本的Rails,Sprockets等使用

  • 以及您在路上找到的内容



正如你已经学到的一样在耙子任务级别,甚至在Rails级别上,不会提供很多信息。甚至使Sprockets本身冗长(见上文)并不能告诉你太多。



如果你想比Sprockets更深入,你可以将猴子补丁发动机和处理器,Sprockets完全链接在一起,使资产管道工作。例如,您可以查看这些组件的日志记录功能:




  • Sass :: Engine (将SASS转换为CSS)

  • Uglifier (JavaScript压缩包装)

  • < ExecJS (在Ruby中运行JavaScript; Sprockets和Uglifier的依赖关系)
  • therubyracer (V8嵌入在Ruby中;由 ExecJS 使用)




但是,我将把这一切都留给读者做一个练习。如果有一个银弹,我一定想知道这个!


I'm working on a Rails 3.2 project and the assets have increased a fair bit in recent months although I wouldn't consider the project to be large. The assets consist of JS (no coffee-script), and SASS files; we've quite a few images but they've been there more less since the early days so I don't think they are a substantial factor. We may have about a dozen libs and most are small, the largest is Jquery UI JS. Deployment is done via Capistrano and it started to become apparent that deploying to staging was significantly faster than to production. To illustrate whilst avoiding factors about different servers and network effects I simply ran the following three commands in sequence on my laptop as follows:

$ time RAILS_ENV=production bundle exec rake assets:precompile
^Crake aborted!
[Note I aborted this run as I felt it was getting stupidly long...]
real    52m33.656s
user    50m48.993s
sys 1m42.165s

$ time RAILS_ENV=staging bundle exec rake assets:precompile
real    0m41.685s
user    0m38.808s
sys 0m2.803s

$ time RAILS_ENV=development bundle exec rake assets:precompile
real    0m12.157s
user    0m10.567s
sys 0m1.531s

So I'm left scratching my head. Why are there such massive differences between the various environments? I can understand the gap between development and staging, but our configs for staging and production are identical. (I should point out that the production compile will complete after about 2hrs!)

Whilst the end result is getting my precompile to be faster, I want to accomplish this by understanding where all the time is going and why there are such big differences between the Rails environments. I've seen other posts about using different compressors and such like but I can't find any information about how to debug these rake tasks to work out where the time is being spent and identify which settings may be causing such dramatic differences.

I don't know what additional information people may need so will update if and when comments ask. TIA

Update: additional information supplied below

config/environments/production.rb and config/environments/staging.rb (they're exactly the same):

MyRailsApp::Application.configure do
  # Code is not reloaded between requests
  config.cache_classes = true

  # Full error reports are disabled and caching is turned on
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true

  # Disable Rails's static asset server (Apache or nginx will already do this)
  config.serve_static_assets = true
  config.static_cache_control = "public, max-age=31536000"
  config.action_controller.asset_host = "//#{MyRailsApp::CONFIG[:cdn]}"

  # Compress JavaScripts and CSS
  config.assets.compress = true

  # Don't fallback to assets pipeline if a precompiled asset is missed
  config.assets.compile = false

  # Generate digests for assets URLs
  config.assets.digest = true

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation can not be found)
  config.i18n.fallbacks = true

  # Send deprecation notices to registered listeners
  config.active_support.deprecation = :notify
end

The base config/application.rb is:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

if defined?(Bundler)
  # If you precompile assets before deploying to production, use this line
  Bundler.require(*Rails.groups(:assets => %w(development test)))
  # If you want your assets lazily compiled in production, use this line
  # Bundler.require(:default, :assets, Rails.env)
end
module MyRailsApp
  CONFIG = YAML.load_file(File.join(File.dirname(__FILE__), 'config.yml'))[Rails.env]

  class Application < Rails::Application

    # Custom directories with classes and modules you want to be autoloadable.
    config.autoload_paths += %W(#{config.root}/lib)
    config.autoload_paths += %W(#{config.root}/app/workers)

    # Configure the default encoding used in templates for Ruby 1.9.
    config.encoding = "utf-8"

    # Configure sensitive parameters which will be filtered from the log file.
    config.filter_parameters += [:password]

    # Enable the asset pipeline
    config.assets.enabled = true

    # Stop precompile from looking for the database
    config.assets.initialize_on_precompile = false

    # Version of your assets, change this if you want to expire all your assets
    config.assets.version = '1.0'

    # Fix fonts in assets pipeline
    # http://stackoverflow.com/questions/6510006/add-a-new-asset-path-in-rails-3-1
    config.assets.paths << Rails.root.join('app','assets','fonts')

    config.middleware.insert 0, 'Rack::Cache', {
      :verbose     => true,
      :metastore   => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"),
      :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
    } # unless Rails.env.production?  ## uncomment this 'unless' in Rails 3.1,
                                      ## because it already inserts Rack::Cache in production

    config.middleware.insert_after 'Rack::Cache', 'Dragonfly::Middleware', :images

    config.action_mailer.default_url_options = { :host => CONFIG[:email][:host] }
    config.action_mailer.asset_host = 'http://' + CONFIG[:email][:host]
  end
end

Gem file:

source 'http://rubygems.org'

gem 'rails', '3.2.13'   
gem 'mysql2'
gem 'dragonfly', '>= 0.9.14'
gem 'rack-cache', :require => 'rack/cache'
gem 'will_paginate'
gem 'dynamic_form'
gem 'amazon_product' # for looking up Amazon ASIN codes of books
gem 'geoip'
gem 'mobile-fu'
gem 'airbrake'
gem 'newrelic_rpm'
gem 'bartt-ssl_requirement', '~>1.4.0', :require => 'ssl_requirement'
gem 'dalli' # memcache for api_cache
gem 'api_cache'
gem 'daemons'
gem 'delayed_job_active_record'
gem 'attr_encrypted'
gem 'rest-client'
gem 'json', '>= 1.7.7'
gem 'carrierwave' # simplify file uploads
gem 'net-scp'

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'therubyracer'
  gem 'sass-rails',   '~> 3.2.3'
  gem 'compass', '~> 0.12.alpha'
  gem 'uglifier', '>= 1.0.3'
  gem 'jquery-fileupload-rails'
end

gem 'jquery-rails'
gem 'api_bee', :git => 'git://github.com/ismasan/ApiBee.git', :ref => '3cff959fea5963cf46b3d5730d68927cebcc59a8'
gem 'httparty', '>= 0.10.2'
gem 'twitter'

# Auth providers
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-google-oauth2'
gem 'omniauth-identity'
gem 'omniauth-readmill'
gem 'bcrypt-ruby', "~> 3.0.0" # required for omniauth-identity
gem 'mail_view'

# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'

# Deploy with Capistrano
group :development do
  gem 'capistrano'
  gem 'capistrano-ext'
  gem 'capistrano_colors'
  gem 'rvm-capistrano'

  # requirement for Hoof, Linux equivalent of Pow
  gem 'unicorn'
end

group :test, :development do  
  gem 'rspec-rails'
  gem 'pry'
  gem 'pry-rails'
end

group :test do
  gem 'factory_girl_rails'
  gem 'capybara'
  gem 'cucumber-rails'
  gem 'database_cleaner'
  gem 'launchy'
  gem 'ruby-debug19'
  # Pretty printed test output
  gem 'shoulda-matchers'
  gem 'simplecov', :require => false
  gem 'email_spec'
  gem 'show_me_the_cookies'
  gem 'vcr'
  gem 'webmock', '1.6'
end

解决方案

This may not entirely answer your question, but I believe it is a decent enough start. As you'll see, the precise answer will depend on the individual application, gem versions and so on.

So. For asset-related work, as you know, Rails uses a library called Sprockets, which in newer versions of Rails is, I believe, hooked into Rails as a Railtie. It initializes a Sprockets "environment" that can do things like look at your asset manifest, load those files, compress them, give the compiled assets sensible names, etc.

By default, that Sprockets::Environment logs its activity to STDERR with a log level of FATAL, which isn't very useful in these situations. Fortunately, the Sprockets::Environment (as of 2.2.2) has a writeable logger attribute that you can patch in via Rails, using an initializer.


So, here's what I suggest, to start:

In config/initializers, create a file, something like asset_logging.rb. In it, put:

Rails.application.assets.logger = Logger.new($stdout)

This overwrites the default logger with one that will spit more information out to STDOUT. Once you've got this set up, then run your asset pre-compilation task:

rake RAILS_ENV=production assets:precompile

And you should see slightly more interesting output, such as:

...
Compiled jquery.ui.core.js  (0ms)  (pid 66524)
Compiled jquery.ui.widget.js  (0ms)  (pid 66524)
Compiled jquery.ui.accordion.js  (10ms)  (pid 66524)
...


But, in the end, the final answer will depend on:

  • how "deep" you want to go with logging this asset stuff
  • what specific version of Rails, Sprockets, etc. you're using
  • and what you find along the way

As you've already learned, log spelunking at the Rake task level, or even at the Rails level, doesn't give much information. And even making Sprockets itself verbose (see above) doesn't tell you too terribly much.

If you wanted to go deeper than Sprockets, you can probably monkey patch the various engines and processors that Sprockets dutifully chains together to make the asset pipeline work. For example, you could look into the logging capabilities of these components:

  • Sass::Engine (converts SASS to CSS)
  • Uglifier (JavaScript compressor wrapper)
  • ExecJS (runs JavaScript in Ruby; a dependency of both Sprockets and Uglifier)
  • therubyracer (V8 embedded in Ruby; used by ExecJS)
  • etc.

But I will leave all that as "an exercise for the reader." If there's a silver bullet, I'd certainly like to know about it!

这篇关于如何调试Rails资源预编译,这是无法忍受的慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆