独角兽介绍
如果你是 Rails 开发人员,你可能听说过 Unicorn,一个可以同时处理多个请求的 HTTP 服务器。
Unicorn 使用折叠过程来实现并行性.由于折叠过程基本上是彼此的副本,这意味着 Rails 应用程序不需要是线程安全的。
如果我们不能确保我们的代码是线程安全的,那么像 Puma这样的并发式 Web 服务器,甚至是利用并发性和并发性的替代 Ruby 实现,如 JRuby和 Rubinius也不会受到质疑。
因此,Unicorn使我们的Rails应用程序即使它们不是线程安全的同时存在,但是,这会带来成本。在Unicorn上运行的Rails应用程序往往会消耗更多的内存。
在本文中,我们将探索利用独角兽的同步性的一些方法,同时控制内存消耗。
使用Ruby 2.0!
如果你正在使用Ruby 1.9,你应该认真考虑切换到Ruby 2.0. 要了解为什么,我们需要了解一点关于。
翻译和复印(CoW)
当一个孩子的过程被折叠时,它与父母的过程完全相同。然而,实际的物理记忆被复制不需要进行。由于它们是准确的副本,孩子和父母的过程可以共享相同的物理记忆。
那么,这与 Ruby 1.9/2.0 和 Unicorn 有什么关系呢?**
回想一下,Unicorn 使用了,理论上,操作系统将能够利用 CoW。不幸的是,Ruby 1.9 并未使此成为可能。更准确地说,Ruby 1.9 的垃圾收集实现并不能使此成为可能。
没有进入太多的细节,就足以说Ruby 2.0垃圾收集器修复了这一点,我们现在可以利用CoW。
调节独角兽的配置
有几个设置,我们可以在config/unicorn.rb中调节,以便从Unicorn中获得尽可能多的性能。
《工人进程》
这会设置要启动的工人流程的数量,重要的是要知道一个流程需要多少内存,这样你就可以安全预算出工人的数量,以免耗尽VPS的RAM。
《时光》
此设置应设置为小数目:通常为 15 到 30 秒是一个合理的数目. 此设置设置为在员工时间结束之前的时间。
首页 > app
此设置为 true
缩短了启动 Unicorn 工人流程的启动时间. 此设置使用 CoW 来预装应用程序,然后将其他工人流程折叠。 但是,有一个 big gotcha。 我们必须特别注意任何插件(如数据库连接)的正确关闭和重新打开。
这里有一个例子:
1before_fork do |server, worker|
2 # Disconnect since the database connection will not carry over
3 if defined? ActiveRecord::Base
4 ActiveRecord::Base.connection.disconnect!
5 end
6
7 if defined?(Resque)
8 Resque.redis.quit
9 Rails.logger.info('Disconnected from Redis')
10 end
11end
12
13after_fork do |server, worker|
14 # Start up the database connection again in the worker
15 if defined?(ActiveRecord::Base)
16 ActiveRecord::Base.establish_connection
17 end
18
19 if defined?(Resque)
20 Resque.redis = ENV['REDIS_URI']
21 Rails.logger.info('Connected to Redis')
22 end
23end
在本示例中,我们会确保连接关闭并在工作者被折叠时重新打开。除了数据库连接外,我们还需要确保其他需要插件的连接得到类似处理。
调节你的独角兽工人的记忆消耗
显然,并非所有的彩虹和独角兽(PUN意图!)如果您的Rails应用程序泄漏了记忆 - Unicorn将使事情变得更糟。
因此,虽然有更多的工人意味着我们的应用程序可以处理更多的入口请求,但我们依赖于我们系统上有多少物理RAM。
即使我们设法插入所有内存泄漏,仍然有不如理想的垃圾收集器来争夺(我指的是MRI实施)。
上面显示的是运行 Unicorn 的 Rails 应用程序,具有内存泄露。
随着时间的推移,内存消耗量将继续增加,使用多个独角兽工人将简单地加速内存消耗的速度,到没有更多的RAM可以储存的地步。
重要的是要注意,这不是独角兽的错,但是,这是一个你迟早会面临的问题。
进入独角兽工人杀手
我遇到的最简单的解决方案之一是 独角兽工杀手宝石。
從《古蘭經》中說:
独角兽工杀手
宝石提供了基于
- 请求的最大数目和
- 过程内存大小(RSS)的自动重新启动,而不会影响任何请求。
请注意,我假设你已经安装和运行了Unicorn。
步骤一:
将独角兽杀手
添加到您的宝藏档案中。 把这个宝石放在独角兽
下方。
1group :production do
2 gem 'unicorn'
3 gem 'unicorn-worker-killer'
4end
步骤二:
点击点击Bundle Install
。
步骤三:
这里来了有趣的部分:找到并打开你的config.ru
文件。
1# --- Start of unicorn worker killer code ---
2
3if ENV['RAILS_ENV'] == 'production'
4 require 'unicorn/worker_killer'
5
6 max_request_min = 500
7 max_request_max = 600
8
9 # Max requests per worker
10 use Unicorn::WorkerKiller::MaxRequests, max_request_min, max_request_max
11
12 oom_min = (240) * (1024**2)
13 oom_max = (260) * (1024**2)
14
15 # Max memory size (RSS) per worker
16 use Unicorn::WorkerKiller::Oom, oom_min, oom_max
17end
18
19# --- End of unicorn worker killer code ---
20
21require ::File.expand_path('../config/environment', __FILE__)
22run YourApp::Application
首先,我们检查我们是否处于生产
环境中,如果是这样,我们将继续执行以下代码。
独角兽工杀手
杀死工人有两个条件:Max请求和Max记忆。
马克斯要求
在本示例中,如果它处理了 500 到 600 个请求,一个工人会被杀死. 请注意,这是一个范围。
麦克斯记忆
在这里,如果它消耗 between 240 到 260 MB 的内存,一个工人就会被杀死。
每个应用程序都有独特的内存要求. 您应该在正常运行期间对应用程序的内存消耗量进行粗略的估计. 这样,您可以更好地估计员工的最低和最大内存消耗量。
一旦您正确配置了所有内容,在部署您的应用程序时,您将注意到更少的错误记忆行为:
注意图表中的子!这就是宝石在做他的工作!
结论
Unicorn为您的 Rails 应用程序提供了无痛的方式来实现同步,无论是线程安全还是不安全,但是,它伴随着增加RAM消耗的成本。
我们已经看到3种方式来调节您的独角兽工人,以获得最大的性能:
使用Ruby 2.0给了我们一个更好的垃圾收集器,允许我们利用抄写语义。 2.在config/unicorn.rb中调整各种配置选项。
资源
- 一个很棒的解释(http://patshaughnessy.net/2012/3/23/why-you-should-be-excited-about-garbage-collection-in-ruby-2-0)如何Ruby 2.0垃圾收集器和抄写语义工作。
- 独角兽配置选项的 完整列表