Puma 是一个简单、快速、多线程、高度并行的 HTTP 1.1 服务器,适用于 Ruby/Rack 应用程序。
Puma 是一个用 Ruby 编写的、用于 Rack 驱动的 HTTP 应用程序的服务器。这是:
Puma 最初设计为 Rubinius 的服务器,也可以与 Ruby (MRI) 和 JRuby 很好地配合。
在 MRI 上,有一个全局 VM 锁 (GVL),可确保一次只有一个线程可以运行 Ruby 代码。但如果您正在进行大量阻塞 IO(例如对 Twitter 等外部 API 的 HTTP 调用),Puma 仍然可以通过允许并行完成 IO 等待来提高 MRI 的吞吐量。真正并行的 Ruby 实现(TruffleRuby、JRuby)没有这个限制。
$ gem install puma
$ puma
如果没有参数,puma 将在名为config.ru
的工作目录中查找rackup (.ru) 文件。
假设系统上安装了 OpenSSL 开发文件,Puma 将安装/编译并支持 ssl 套接字。
如果系统没有安装 OpenSSL 开发文件,Puma 将安装/编译,但不允许 ssl 连接。
Puma 是 Rails 的默认服务器,包含在生成的 Gemfile 中。
使用rails
命令启动服务器:
$ rails server
使用rails server
时,许多配置选项和 Puma 功能不可用。建议您使用 Puma 的可执行文件:
$ bundle exec puma
您可以从命令行使用 Puma 运行 Sinatra 应用程序,如下所示:
$ ruby app.rb -s Puma
然而,为了使用配置文件(如puma.rb
实际配置 Puma,您需要使用puma
可执行文件。为此,您必须将rackup 文件添加到您的Sinatra 应用程序中:
# config.ru
require './app'
run Sinatra :: Application
然后您可以使用以下方式启动您的应用程序:
$ bundle exec puma
Puma 提供多种选择。有关 CLI 选项的完整列表,请参阅puma -h
(或puma --help
),或参阅Puma::DSL
或 dsl.rb。
您还可以找到几个配置示例作为测试套件的一部分。
出于调试目的,您可以设置环境变量PUMA_LOG_CONFIG
的值,加载的配置将作为引导过程的一部分打印。
Puma 使用线程池。您可以使用-t
(或--threads
)标志设置池中可用线程的最小和最大数量:
$ puma -t 8:32
Puma 将根据当前流量自动调整线程数量,从最小到最大。当前默认值为0:16
,MRI 上的默认值为0:5
。请随意尝试,但请注意不要将最大线程数设置为很大的数字,因为您可能会耗尽系统上的资源(或在使用 MRI 时导致全局 VM 锁争用)。
请注意,Puma 还出于内部目的(例如处理慢速客户端)自行创建线程。因此,即使您指定 -t 1:1,预计应用程序中会创建大约 7 个线程。
Puma 还提供“集群模式”。集群模式从主进程中fork
工人。每个子进程仍然有自己的线程池。您可以使用-w
(或--workers
)标志调整工作人员的数量:
$ puma -t 8:32 -w 3
或者使用WEB_CONCURRENCY
环境变量:
$ WEB_CONCURRENCY=3 puma -t 8:32
请注意,线程仍然在集群模式下使用,并且-t
线程标志设置是针对每个工作线程的,因此-w 2 -t 16:16
将总共生成 32 个线程,每个工作进程中有 16 个线程。
如果WEB_CONCURRENCY
环境变量设置为"auto"
并且concurrent-ruby
gem 在您的应用程序中可用,Puma 会将工作进程计数设置为可用处理器的结果。
有关线程和进程计数设置的权衡的深入讨论,请参阅我们的文档。
在集群模式下,Puma 可以“预加载”您的应用程序。这会在分叉之前加载所有应用程序代码。预加载通过称为写入时复制的操作系统功能减少应用程序的总内存使用量。
如果WEB_CONCURRENCY
环境变量设置为值 > 1(且未指定--prune-bundler
),则默认情况下将启用预加载。否则,您可以从命令行使用--preload
标志:
$ puma -w 3 --preload
或者,如果您使用配置文件,则可以使用preload_app!
方法:
# config/puma.rb
workers 3
preload_app!
预加载不能与分阶段重启一起使用,因为分阶段重启会逐一杀死并重启worker,而预加载会将master的代码复制到worker中。
当使用集群模式时,Puma 的配置 DSL 提供before_fork
和on_worker_boot
钩子,分别在主进程 fork 和子进程启动时运行代码。
建议将这些钩子与preload_app!
,否则应用程序(例如Rails
)加载的常量在钩子内将不可用。
# config/puma.rb
before_fork do
# Add code to run inside the Puma master process before it forks a worker child.
end
on_worker_boot do
# Add code to run inside the Puma worker process after forking.
end
此外,还有一个on_refork
钩子,仅在fork_worker
模式下使用,当worker 0子进程派生孙子worker时:
on_refork do
# Used only when fork_worker mode is enabled. Add code to run inside the Puma worker 0
# child process before it forks a grandchild worker.
end
重要的是,当 Ruby fork 子进程时请注意以下注意事项:
SocketError
、 Errno::EPIPE
和EOFError
。因此,我们建议如下:
before_fork
和on_refork
断开父进程的socket连接,这样它们就不会被意外复制到子进程。on_worker_boot
重新启动分叉子进程上的任何后台线程。 Puma 的配置 DSL 提供主进程生命周期挂钩on_booted
、 on_restart
和on_stopped
,可用于指定在每个事件上运行的代码块:
# config/puma.rb
on_booted do
# Add code to run in the Puma master process after it boots,
# and also after a phased restart completes.
end
on_restart do
# Add code to run in the Puma master process when it receives
# a restart command but before it restarts.
end
on_stopped do
# Add code to run in the Puma master process when it receives
# a stop command but before it shuts down.
end
如果 Puma 在应用程序上下文之外遇到错误,它将以 400/500 和简单的文本错误消息进行响应(请参阅Puma::Server#lowlevel_error
或 server.rb)。您可以为此场景指定自定义行为。例如,您可以向第三方错误跟踪服务(在此示例中为 rollbar)报告错误:
lowlevel_error_handler do | e , env , status |
if status == 400
message = "The server could not process the request due to an error, such as an incorrectly typed URL, malformed syntax, or a URL that contains illegal characters. n "
else
message = "An error has occurred, and engineers have been informed. Please reload the page. If you continue to have problems, contact [email protected] n "
Rollbar . critical ( e )
end
[ status , { } , [ message ] ]
end
使用-b
(或--bind
)标志将 Puma 绑定到套接字:
$ puma -b tcp://127.0.0.1:9292
要使用 UNIX 套接字而不是 TCP:
$ puma -b unix:///var/run/puma.sock
如果需要更改UNIX套接字的权限,只需添加一个umask参数:
$ puma -b 'unix:///var/run/puma.sock?umask=0111'
需要一点安全感吗?使用 SSL 套接字:
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'
localhost
gem,供开发使用): Puma 支持使用localhost
gem 进行自签名证书。如果您想在本地使用带有 SSL 的 Puma,并且自签名证书适用于您的用例,这尤其有用。目前,该集成只能用于 MRI。
当localhost
gem 在development
环境中加载时,Puma 会自动配置 SSL:
将 gem 添加到您的 Gemfile 中:
group ( :development ) do
gem 'localhost'
end
并使用捆绑器隐式要求它:
require "bundler"
Bundler . require ( :default , ENV [ "RACK_ENV" ] . to_sym )
或者,您可以在配置文件config/puma/development.rb
、 config/puma.rb
中要求 gem,或通过-C
cli 选项设置:
require 'localhost'
# configuration methods (from Puma::DSL) as needed
此外,Puma 必须监听 SSL 套接字:
$ puma -b ' ssl://localhost:9292 ' -C config/use_local_host.rb
# The following options allow you to reach Puma over HTTP as well:
$ puma -b ssl://localhost:9292 -b tcp://localhost:9393 -C config/use_local_host.rb
要使用或避免 TLSv1.2 及更低版本的特定 SSL 密码,请使用ssl_cipher_filter
或ssl_cipher_list
选项。
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_cipher_filter=!aNULL:AES+SHA'
$ puma -b 'ssl://127.0.0.1:9292?keystore=path_to_keystore&keystore-pass=keystore_password&ssl_cipher_list=TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA'
要配置可用的 TLSv1.3 密码套件,请使用ssl_ciphersuites
选项(不适用于 JRuby)。
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_ciphersuites=TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256'
有关密码过滤器格式和密码套件的完整列表,请参阅 https://www.openssl.org/docs/man1.1.1/man1/ciphers.html。
使用no_tlsv1
选项禁用 TLS v1:
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&no_tlsv1=true'
要启用 OpenSSL 提供的验证标志,请使用verification_flags
(不适用于 JRuby):
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&verification_flags=PARTIAL_CHAIN'
您还可以设置多个验证标志(通过用逗号分隔它们):
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&verification_flags=PARTIAL_CHAIN,CRL_CHECK'
可用标志列表: USE_CHECK_TIME
、 CRL_CHECK
、 CRL_CHECK_ALL
、 IGNORE_CRITICAL
、 X509_STRICT
、 ALLOW_PROXY_CERTS
、 POLICY_CHECK
、 EXPLICIT_POLICY
、 INHIBIT_ANY
、 INHIBIT_MAP
、 NOTIFY_POLICY
、 EXTENDED_CRL_SUPPORT
、 USE_DELTAS
、 CHECK_SS_SIGNATURE
、 TRUSTED_FIRST
、 SUITEB_128_LOS_ONLY
、 SUITEB_192_LOS
、 SUITEB_128_LOS
、 PARTIAL_CHAIN
、 NO_ALT_CHAINS
、 NO_CHECK_TIME
(参见https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_hostflags.html#VERIFICATION-FLAGS)。
要启用加密 SSL 密钥的运行时解密(不适用于 JRuby),请使用key_password_command
:
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&key_password_command=/path/to/command.sh'
key_password_command
必须:
例如:
#! /bin/sh
echo " this is my password "
key_password_command
可以与key
或key_pem
一起使用。如果密钥未加密,则不会调用可执行文件。
Puma 有一个内置的状态和控制应用程序,可用于查询和控制 Puma。
$ puma --control-url tcp://127.0.0.1:9293 --control-token foo
Puma 将在本地主机端口 9293 上启动控制服务器。对控制服务器的所有请求都需要包含控制令牌(在本例中为token=foo
)作为查询参数。这允许简单的身份验证。查看Puma::App::Status
或 status.rb 以查看状态应用程序可用的内容。
您还可以通过pumactl
与控制服务器交互。此命令将重新启动 Puma:
$ pumactl --control-url 'tcp://127.0.0.1:9293' --control-token foo restart
要查看pumactl
选项列表,请使用pumactl --help
。
您还可以提供带有-C
(或--config
)标志的配置文件:
$ puma -C /path/to/config
如果未指定配置文件,Puma 将在config/puma.rb
中查找配置文件。如果指定了环境(通过--environment
标志或通过APP_ENV
、 RACK_ENV
或RAILS_ENV
环境变量),Puma 在config/puma/<environment_name>.rb
中查找配置文件,然后回退到config/puma.rb
。
如果您想阻止 Puma 在这些位置查找配置文件,请包含--no-config
标志:
$ puma --no-config
# or
$ puma -C "-"
设置环境的其他副作用是是否显示堆栈跟踪(在development
或test
中),并且设置 RACK_ENV 可能会影响寻找此值以更改其行为的中间件。默认的 puma RACK_ENV 值为development
。您可以在Puma::Configuration#puma_default_options
或 configuration.rb 中查看所有配置默认值。
查看Puma::DSL
或 dsl.rb 以查看所有可用选项。
Puma 具有自行重启的能力。当可用时(MRI、Rubinius、JRuby),Puma 会执行“热重启”。这与Unicorn和NGINX中可用的功能相同,可以在重新启动之间保持服务器套接字打开。这可确保重新启动时不会丢弃挂起的请求。
有关更多信息,请参阅重新启动文档。
Puma 对多种信号做出反应。有关在 Puma 中使用 UNIX 信号的详细指南可以在信号文档中找到。
某些平台不支持所有 Puma 功能。
对于 MRI 版本 2.2.7、2.2.8、2.2.9、2.2.10、2.3.4 和 2.4.1,您可能会看到stream closed in another thread (IOError)
。这可能是由 Ruby bug 引起的。它可以用 gem https://rubygems.org/gems/stopgap_13632 修复:
if %w( 2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1 ) . include? RUBY_VERSION
begin
require 'stopgap_13632'
rescue LoadError
end
end
Puma 通过外部 gem 支持 Capistrano。
此外,Puma 通过 puma-daemon ruby gem 支持内置守护进程。 gem 恢复了从 Puma 版本 5 开始删除的daemonize
选项,但仅适用于 MRI Ruby。
在 Puma 中使用进程监视器是很常见的。现代进程监视器(如 systemd 或 rc.d)提供连续监视和重新启动,以提高生产环境的可靠性:
社区指南:
在贡献指南中查找贡献详细信息。
Puma 的版权归 Evan Phoenix 及其贡献者所有,并根据 BSD 3-Clause 许可证获得许可。有关详细信息,请参阅随附的许可证文件。