相对于一次只处理一个请求的 Web 应用程序,并发处理请求的 Web 应用程序,能够更高效的使用动态资源。Puma 是和 Unicorn 相竞争的 Web 服务器,它能够处理并发请求。 Puma 使用线程,以及工作者进程,能够更多的利用可用的 CPU。在 Puma 中,如果整个基础代码是线程安全的,那么你可用利用线程。否则,在使用 Puma 的时候,你只能使用工作者进程进行拓展。 这个指南会简单介绍,使用 Puma Web 服务器部署新的 Rails 应用程序到 Heroku 平台。 对于基本的Rails 安装,请参看Rails 入门. 在部署到生产环境之前,请先在准生产环境(staging environment)测试。 |
将 Puma 加入应用程序Gemfile首先,将 Puma 添加到应用程序的 Gemfile 文件:
Procfile设置 Puma 为应用程序 Procfile 文件中Web 进程的服务器。你可以在一行中设置大多数值:
但是,我们推荐生成一个配置文件:
请确保 Procfile 大写正确,并且签入到 git。 Config在 config/puma.rb 中为 Puma 创建一个配置文件,或者任选一个目录。对于一个简单的 Rails 应用程序,我们推荐下面的基本配置:
你同样必须保证,Rails 应用程序,为所有的线程和工作者(这将在后续介绍),提供足够的连接池中的可用数据库连接。 如果你的应用程序不是线程安全的,你将只能使用工作者(worker)。设置最小和最大线程数为1:
查看下面关于线程安全的部分,以获取更多信息。 |
工作者(Worker)
Puma 在每个dyno内部生成多个操作系统进程,以允许 Rails应用程序支持多个并发请求。 用Puma的术语来说,它们称为工作者进程(不要和Heroku工作者进程相混淆,Heroku工作者进程是在它们自己的dyno里面运行的)。工作者进程处于操作系统级别的相互隔离,因此,无需保持线程安全。 如果你在使用JRuby或者Windows,多进程模式无法工作,因为JVM和Windows不支持多进程。如果你在使用JRuby或者Windows,请从配置中忽略这行。 每个工作者进程使用额外的内存。这限制了在一个dyno中运行的进程数。依据典型的Rails内存占用,在1x dyno中,你大概可以运行2-4个Puma工作者进程。我们推荐在配置变量中指定这个数值,以便于应用程序更快的调整。请监控应用程序日志的R14错误(内存配额溢出),使用 附加日志(logging addons)或者 heroku日志. |
线程
Puma 可以一个线程服务一个请求,这些线程来自内部的线程池。对于Web应用程序来说,这让Puma获得了额外的并发性。宽泛的说,工作者消耗更多的RAM,线程消耗更多的CPU,两者都提供了更高的并发性。 在MRI之中,存在一个全局解释锁(Global Interpreter Lock (GIL)),它确保,在任意时刻,只有一个线程在运行。IO操作,如数据库调用,文件系统交互,或者外部HTTP调用,都不会锁定GIL。大多数Rails应用程序大量使用IO,因此,增加额外的线程将允许Puma处理多线程,获得更大的吞吐量。JRuby和Rubinius也从Puma受益。这些Ruby实现并没有GIL,无论什么情况下,它们都会以并行的方式运行所有的线程。 Puma允许配置线程池中的线程数,通过设置min和max来控制每个Puma实例使用的线程数。当处于非负载状态下,最小线程可以让应用程序降低资源消耗。这个特性在Heroku上并不需要,因为应用程序可以消耗给定的dyno上的所有资源。我们推荐将min和max设置为相同的值。 每个Puma工作者都将可以生成指定数目的线程。 |
预加载应用程序(Preload app)
预加载应用程序减少了Puma工作者进程启动的时间,同时,可以使用on_worker_boot调用,管理每个单一工作者的外部连接。在上面的配置中,这些调用,用来为每个工作者进程和Postgres正常建立连接。 关于工作者引导(On worker boot)这里的on_worker_boot块,是在工作者生成后运行的,但是,是在它开始接受请求之前。这个块对于连接到不同的服务非常有用,因为在多个进程之间,连接不能共享。这和Unicorn的after_fork块非常类似。它只在使用多进程模式(也就是,有指定的工作者)的情况下才需要。 如果你正在使用Rails 4.1+,你可以使用database.yml 来设置连接池的大小 ,按照下面的来做即可:
否则,你必须使用重连代码:
|
我们在默认配置里设置数据库数据池尺寸。需要更多信息请阅读在Ruby中的并发与数据库连接ActiveRecord(Concurrency and Database Connections in Ruby with ActiveRecord)。我们也要确定创建的新连接连到数据库哪里。 很多数据存储诸如 Postgres, Redis 或者 memcache,都需要你重连接。在预加载那一节,我们会展示如何重连接 Active Record。如果你使用 Resque,它连接到 Redis 时,你将需要重连接:
如果在你启动应用时没有成功连接,可以查阅 gem 文档,了解如何在这个模块上重新连接。 |
Rackup
使用rackup命令,告知Puma如何启动rack应用程序。这应该在应用程序的config.ru文件中,这个文件是新建一个项目时,由Rails自动生成的。 在新版本的Puma中,这行是不需要的。 端口(Port)
这是Puma将会绑定的端口。当Web进程启动时,HeroKu会设置ENV['PORT']。本地默认设置端口为3000,以匹配Rails的默认值。 环境(Environment)
这是设置Puma的环境。在Heroku之中,ENV['RACK_ENV']会默认设置为'production'。 超时(Timeout)在Puma之中,没有请求超时。 Heroku 路由将 超过30秒的请求设为超时。 尽管会返回错误给客户端,但是Puma仍会继续处理这个请求,就像路由没有采用任何方式,提前告知Puma,此请求已经终止了一样。 增加 Rack 超时 到项目的gem,然后在初始化器(initializer)中设置低于30的值:
现在,任何超过20秒的请求都会被终止,同时栈跟踪信息会输出到日志之中。栈跟踪信息,有助于查明应用程序的哪个部分会导致超时,以便修复。 |
样例代码开源codetriage.com项目使用 Puma 并且你可以在这个配置文件中看 Puma。 线程安全线程安全代码可以被多线程运行并且无错误。并不是多有的 Ruby 代码是 threadsafe 代码,而且决定你的代码和所有的库文件在多线程上运行是有点困难的。 直到 Rails4 版本,出现了一个伴有疑虑的线程安全互通模式。纵然 Rails 是线程安全,但是它不保证你的代码能运行在安全线程上。如果你还没有在线程化的环境下运行你的应用,我们建议部署以及设置 MIN_THREADS 和 MAX_THREADS 二者为1:
你仍可以通过增加工作器来获得并发。因为工作器运行在不同的进程上,而且不会共享内存,不是安全线程的代码可以运行在多工作器进程上。 一旦你在工作器上运行了应用,你可以增加在工作台上的线程数量以及部署为2:
你需要监视异常情况并且查找错误,像(致命的)死锁,竞争条件以及能够修改全局或者共享变量的位置。 |
并发程序的bug很难被侦测和修复,因此必须确定你的应用在部署前已经被充分地测试。如果让你的应用线程安全,是非常值得的,比起单独使用工作者(worker),使用扩展的Puma线程和工作者(worker)取得了更多的生产力. 一旦你对应用的预期行为有信心,你可以增加你的线程数。 优化线程数,我们被推荐着眼于请求的延迟。如果你的应用是在负载下额外的线程,在一定程度上,将会减少请求的延迟。一旦额外的新线程不再给你的应用提升可测的请求时间,那就不再需要添加额外的线程。 |
数据库连接随着应用程序并发量的增加,你会需要更多的与数据库的连接。确定每个应用需要连接数的一个准则是用PUMA_WORKERS乘以MAX_THREADS。这将会确定每个dyno消费多少个连接。 Rails维护自己的数据库连接池,同时对于每个工作者进程新建一个连接池。一个工作者的线程将会在同一个连接池中操作。确保你的Rails数据库连接池中有足够的连接,这样就有MAX_THREADS个数的连接可以使用了。如果你看见这个错误:
这表明你的Rails连接池太小了。想要深入了解该话题,请阅读dev center中的文章并发和数据库连接。 |
慢客户端一个慢客户端发送和接收数据是非常慢的。举个例子,一个 app 接收用户上传的手机图片网络类型非 WiFi,4G 或其他网络类型。这种连接可能导致某些服务器拒绝服务,如 unicorn,整个上传过程必须等待请求完成。 puma 可以允许多个慢客户端连接而不需要一个被请求的事件。因此,puma 处理慢客户端非常快速。你要是有满客户端的情况,使用 puma 是不错的选择。 |
Backlog在Puma中可以设置 “backlog” 的值。这个值是,Puma在开始拒绝HTTP请求之前,socket队列的请求数。它的默认值为1024,建议不要修改或降低这个值。减小这个值,看起来像是一个好主意,这样,当一个dyno处于繁忙之中的时候,请求可以发送到不那么繁忙的dyno。当Heroku重新路由(reroute)一个弹回(bounced)请求的时候,它假定整个应用程序已经饱和。此时,每个连接延迟5秒,因此,每个请求惩罚性的自动延迟5秒。你可以获取关于 路由运转 的更多信息。另外,当一个dyno开始弹回(bounce)请求的时候,很可能是由于负载增加,所有的dyno都将弹回请求。重复弹回同样的请求,将导致客户端更高的错误率。 一个随意的高backlog值允许dyno处理陡然增加的请求。降低这个值,不能明显加速应用程序,却更可能导致客户端更多的失败请求。Heroku建议 不要 设置backlog的值,而使用其默认值。 |
本文转自:开源中国社区 [http://www.oschina.net]
本文标题:使用 Puma Web 服务器部署 Rails 应用
本文地址:http://www.oschina.net/translate/deploying-rails-applications-with-the-puma-web-server
参与翻译:gones945, 无若, postdep, king_dust, fr000
英文原文:Deploying Rails Applications with the Puma Web Server