Puma: 为并发而生的Ruby Web服务器

描述

Puma是一个简单,快速,线程式,并且支持HTTP1.1高并发的Ruby/Rack服务器。在开发环境和生产环境中都可以使用Puma。

为速度和并发而生

Puma是一个应用于Ruby Web应用,简单,快速,支持高并发的HTTP1.1服务器。它可以和任何支持Rack的应用配合使用,是WebrickMongrel的替代选择。它最初被设计为Rubinius的服务器,但是配合JRubyMRI使用性能同样出色。在开发环境和生产环境中都可以使用Puma。
在面纱之下,Puma使用经过C语言优化的Ragel扩展(继承自Mongrel)来解析请求,用一种轻便的方式进行快速,精确的HTTP 1.1协议解析。然后使用内部线程池的线程来处理请求。这使得Puma可以为你的Web应用提供真正的并发。
Rubinius 2.0后,Puma将使用真正的线程来利用CPU的所有核,因此不用再使用多进程来提高性能。可以期待我们将获得和JRuby相似的性能。
对于MRI,因为全局锁(GIL)的限制,每次只能运行一个线程。但是对于大量阻塞IO的操作(例如调用Twitter API的HTTP请求),Puma还是通过允许阻塞IO操作并行运行提高了MRI的性能(像Thin这种以EventMachine为基础的服务器关闭了这个特性,需要自己使用第三方库)。根据你的使用情况,为了获得最佳性能,强烈建议使用实现了真正多线程的Ruby版本,例如 RubiniusJRuby

快速开始

开始使用Puma最简单的方式是通过RubyGems安装它。简单的运行以下命令即可:

$ gem install puma

现在PATH中应该有了puma命令,可以试着在你的Rack应用根目录中运行:

$ puma app.ru

插件

Puma 3.0增加了对插件的支持,可以使用这些插件来增强配置项。
来看两个核心插件

  • tmp_restart: 如果tmp/restart.txt被touch,则重启服务器。
  • heroku:封装了在Heroku上使用Puma的默认配置。
    插件可以通过在puma配置文件(例如config/puma.rb)中添加plugin "插件名"来指定,例如plugin "heroku"。
    也可以通过引用路径来指定,要使用heroku插件只需要引用puma/plugin/heroku即可。这样一来gem可以提供多个插件(或者由不相关的gem提供puma插件)。
    Puma内置了tmp_restart插件,可以直接使用。
    可以通过将puma-heroku添加到Gemfile中来安装使用heroku插件。

API

目前,插件可以使用两个钩子方法:startconfig
start在服务器启动之后运行,可以支持插件启动一些其他的服务来增强puma。
config在服务器被配置时执行,并且会被传递一个Puma::DSL对象用来添加增强的配置项。
Puma::Plugin中的所有公共方法都是插件可以使用的公共API。
未来,会加入更多的钩子方法和API。

高级配置

Sinatra

可以通过命令行使用Puma运行Sinatra应用

$ ruby app.rb -s Puma

或者配置应用默认使用Puma运行

require 'sinatra'
configure { set :server, :puma }

如果使用Bundler的话,请将Puma加入到Gemfile当中(见下文)。

Rails

首先,确定Puma在Gemfile中

gem 'puma'

然后使用rails命令启动服务

$ rails s Puma

Rails 5.0之后puma是默认服务器,直接执行rails s即可。

Rackup

将Puma作为参数传递给rackup来启动应用

$ rackup -s Puma

也可以通过将下面的命令加到config.ru文件的第一行,使应用默认以Puma启动

#\ -s puma

配置

Puma提供了多种选项来控制服务器的操作,执行puma -h(或puma --help)了解详情。

线程池

Puma使用可配置的动态线程池。可以通过配置-t(或--threads)来指定可用的最小和最大线程数。

$ puma -t 8:32

Puma会基于流量自动调节线程数,范围从最小值到最大值。当前默认设置是0:16。你可以修改这个设置,但是当心不要将最大线程数设置的过大,这样有可能会耗尽系统资源。

集群模式

Puma2提供了集群模式,支持使用fork出的进程配合已有的多线程功能来并发的处理请求。可以通过配置-w(或--workers)来指定workers数量:

$ puma -t 8:32 -w 3

在提供真正多线程的Ruby实现中,应该将worker数设置为可用的核数。注意多线程仍然在集群模式下可用,使用-t来为每一个worker设置线程数,所以-w 2 -t 16:16就是32个线程。
在集群模式下,�Puma可以"预加载"应用。在fork之前加载整个项目代码。预加载通过利用操作系统的copy-on-write特性降低了内存的使用量(Ruby2.0以上)。指定--preload启动puma:

puma -w 3 --preload

如果使用配置文件,则在文件中指定preload_app!方法:

# config/puma.rb
workers 3
preload_app!

另外可以在配置文件中指定每个worker启动时需要执行的代码块:

# config/puma.rb
on_worker_boot do
  # configuration here
end

这段代码用于在应用启动之前设置进程,避免将puma相关的设置嵌入应用代码中。比如,可以在这里设置worker启动的日志信息。
如果你需要预加载项目并且在使用ActiveRecord,那么建议你在这里设置连接池:

# config/puma.rb
on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

在其上还可以指定fork之前需要执行的代码块:

# config/puma.rb
before_fork do
  # configuration here
end

预加载不能和分步重启一起使用,因为分步重启需要一个个杀死worker再一个个重启,而preload_app是将master加载的代码拷贝到其他workers。

Puma的启动方式

With Rubinius 2.0, Puma will utilize all cores on your CPU with real threads

Global Interpreter Lock (GIL)

puma app.ru
puma
rails s Puma
puma -C config/puma.rb
puma -b tcp://127.0.0.1:9292
puma -b unix:///var/run/puma.sock
puma --control tcp://127.0.0.1:9293 --control-token foo #通过控制服务器启动
pumactl restart

使用touch重启puma不生效?

touch tmp/restart.txt

设置线程数(动态线程池)

Puma will automatically scale the number of threads, from the minimum until it caps out at the maximum, based on how much traffic is present. The current default is 0:16. Feel free to experiment, but be careful not to set the number of maximum threads to a very large number, as you may exhaust resources on the system (or hit resource limits).

除了线程之外,可以通过配置worker开启集群模式
worker的数量应该与计算机核数相等

如果使用集群模式,可以选择在启动worker之前预加载应用。为了有效利用copy on write功能,这一步是必须的。
只需要在invocation中指定--preload即可。
如果使用配置文件,使用preload_app!方法

If you're preloading your application and using ActiveRecord, it's recommended that you setup your connection pool here

When you use preload_app, all of your new code goes into the master process, and is then copied into the workers

需要快速启动

General rule is to use preload_app when your workers die often and need fast starts. If you don’t have many workers, you probably should not use preload_app.

Note that preload_app can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and preload_app is all about copying the code of master into the workers.

控制/状态服务器

puma有一个内置的状态/控制服务器用来查询管理puma。

puma --control tcp://127.0.0.1:9293 --control-token foo

This directs Puma to start the control server on localhost port 9293. Additionally, all requests to the control server will need to include token=foo as a query parameter. This allows for simple authentication.

指定配置文件启动

By default, if no configuration file is specified, Puma will look for a configuration file at config/puma.rb

puma -e staging

重启

Puma includes the ability to restart itself allowing easy upgrades to new versions. When available (MRI, Rubinius, JRuby), Puma performs a "hot restart". This is the same functionality available in unicorn and nginx which keep the server sockets open between restarts. This makes sure that no pending requests are dropped while the restart is taking place.

重启的两种方式:

  • 向puma进程发送SIGUSR2信号
  • 使用状态服务器并且发送/restart指令

No code is shared between the current and restarted process, so it should be safe to issue a restart any place where you would manually stop Puma and start it again.
If the new process is unable to load, it will simply exit. You should therefore run Puma under a process monitor (see below) when using it in production.

If you perform a lot of database migrations, you probably should not use phased restart and use a normal/hot restart instead (pumactl restart). That way, no code is shared while deploying (in that case, preload_app might help for quicker deployment, see below).

pumactl

pumactl is a simple CLI frontend to the control/status app described above. Please refer to pumactl --help for available commands.

app_root = ‘/home/hashtagbe/app/current’
pidfile “#{app_root}/tmp/pids/puma.pid”
state_path “#{app_root}/tmp/pids/puma.state”
bind ‘unix:///home/hashtagbe/app/shared/tmp/sockets/puma.sock’
daemonize true
environment ‘staging’
port 9291
workers 2
threads 1,4
preload_app!

on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

before_fork do
  ActiveRecord::Base.connection_pool.disconnect!
end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,289评论 6 344
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,543评论 25 707
  • 学 车 三 记 刚刚写下这个标题的人,从报名到拿驾照历时11个月,科二补考2次,科三补考3次。驾校深夜的弯月冷冷俯...
    许米娜是也阅读 222评论 4 3
  • 几乎没有入睡的夜晚,绕不开的心事重重。对比昨天的自己。我想,不得不承认某人的影响力。 开始会觉得是“小朋友”,但是...
    都灵江阅读 134评论 0 0