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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,954評論 6 342
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,288評論 25 708
  • 學 車 三 記 剛剛寫下這個標題的人,從報名到拿駕照歷時11個月,科二補考2次,科三補考3次。駕校深夜的彎月冷冷俯...
    許米娜是也閱讀 248評論 4 3
  • 幾乎沒有入睡的夜晚,繞不開的心事重重。對比昨天的自己。我想,不得不承認某人的影響力。 開始會覺得是“小朋友”,但是...
    都靈江閱讀 143評論 0 0