由于GEM_HOME中的requires,启动Ruby应用程序非常缓慢

6
我目前正在开发一个Ruby应用程序,但运行非常(非常!)缓慢。到目前为止,我已经尝试了一些方法,可以将问题缩小到一个主要问题:Ruby试图在$LOAD_PATH中的每个目录中查找它所需的内容。基本上,我观察到的是,Ruby正在浏览大量文件,试图查看它是否存在所需的内容。如果找不到它们,它会进入下一个目录。好在我可以通过strace看到这种情况。有很多这样的输出:
open("/boa_proj_build/nsteen/.gem/gems/i18n-0.7.0/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/thread_safe-0.3.5/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/tzinfo-1.2.2/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/minitest-5.8.2/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/activesupport-4.2.4/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/climate_control-0.0.3/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/cocaine-0.5.7/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/boa_loggable-0.2.2/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/ruby_expect-1.6.0/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/cctools-3.0.1/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/git-1.2.9.1/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/naught-1.1.0/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/symbolizer-0.0.1/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/settingslogic-2.0.9/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/memoist-0.12.0/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/highline-1.7.8/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/boa_proj_build/nsteen/.gem/gems/commander-4.3.5/lib/commander/help_formatters/base.rb", O_RDONLY|O_CLOEXEC) = 8

正如您所看到的,它正在查找一些目录来找到它的要求。

通过使用测试应用程序跟踪此操作,过滤 ENOENT 错误并计算出现次数,将显示以下相关输出:

vdi9442:/boa_proj_build/nsteen/$ strace packager --version 2>&1 | grep ENOENT | wc -l
3454261

当然,350万是很多的。这将导致大约5分钟(没有strace时约一半)的加载时间,然后才会输出其版本号(来自commander gem的默认功能)。
我已经删除了整个gem主目录,并再次运行了测试,它立即变得更快了,但我可以看到它再次查找那些少量的gems(像commander这样的依赖项),但只有几千次出现,而不是350万次。
我的gem env看起来像这样:
  - GEM PATHS:
     - /boa_proj_build/nsteen/.gem
     - /home/nsteen/.gem/ruby/2.1.0
     - /cadappl/ruby/2.1.1/ruby/lib/ruby/gems/2.1.0

看起来Ruby正在遍历我的整个加载路径,以满足一些依赖关系。这没问题,但这变得有点荒谬了。

有人知道发生了什么吗? 我怀疑这不是想要的/默认行为?

有人知道发生了什么吗?我该如何加速它?


Rubygems有时候可能会非常慢。这篇来自Sitepoint的文章详细阐述了其中的原因以及可以采取的解决方法。 - wspurgin
你是否已经好奇地使用了bundler - wspurgin
@wspurgin 那不可能是问题。它正在迭代已安装的 gem。另外,是的。使用 Bundler。 - Niek van der Steen
Rubygems是一个框架,当你使用require时,它负责查找你的gem以及安装等功能。运行method("require").source_location,你会在rubygems/core_ext下看到它,所以它可能仍然是问题的原因。 - wspurgin
2个回答

4

有很多种方法来解决这个问题,包括我不会介绍的gemrc文件。以下是您可以选择的一些选项:

解决方案1:

另一个答案是正确的,但我想扩展一下这个主题,因为它似乎是人们经常面临的问题之一。RVM可以帮助。一个特定的功能是为此而设计的,即gemsets。我个人已经转移到了rbenv,并且没有回头看过。Rbenv对您的环境干扰要小得多,但它们都很棒。您可以在RVM和rbenv中使用gemsets,以限制可用于应用程序的宝石。您还可以创建特定于应用程序的gemset。使用gemset,您的应用程序将查找一个位置,并仅加载它使用过的宝石。它不会与其他应用程序的gemsets混合在一起。最好的事情是自动的,您只需要设置一次它就会在您进入该目录时自动切换。它还与支持Ruby的Rubymine和其他IDE/编辑器集成。

解决方案2:

GEM_PATH环境变量设置为其中一个位置。这将覆盖所有其他内容,因此至少会停止在多个位置查找。

解决方案3(应用程序加载优化):

您可能需要查看的一些内容是,如果您正在使用bundler,则可能希望仅在需要时才引用特定的使用宝石。例如,如果您在应用程序的一个模块中使用曲线宝石,请将其添加到加载您的模块的文件顶部:

require 'curb'

并且更改您的Gemfile,使加载curb的行看起来像这样:

gem 'curb', require: false

应用程序启动时加载的宝石数量越少,加载速度就越快。自动要求功能非常好用,但经常被忽视和想当然地使用。这可能会带来惊人的差异。


非常感谢您详细的回答! - Niek van der Steen
抱歉,我有点半发泄了。这些只是我认为可能会看到这个问题的人想知道的事情。 - Bassel Samman
不要抱歉,非常感激 :) - Niek van der Steen
谢谢,我很高兴它有帮助。 - Bassel Samman

1
我建议使用rvm管理Ruby版本和gem。使用rvm,您可以创建特定于应用程序的gemsets和Ruby版本。
我希望这能解决您的问题。

https://rvm.io/


我们实际上正在寻找一种使用'gem install'的解决方案,因为我们的用户需要安装这个工具。 - Niek van der Steen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接