

快就是帥,加速你的 Rails 專案啟動時間
source link: https://blog.niclin.tw/2018/07/21/%E5%BF%AB%E5%B0%B1%E6%98%AF%E5%B8%A5%E5%8A%A0%E9%80%9F%E4%BD%A0%E7%9A%84-rails-%E5%B0%88%E6%A1%88%E5%95%9F%E5%8B%95%E6%99%82%E9%96%93/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Nic Lin's Blog
喜歡在地上滾的工程師
速度快就像雷神降臨一樣帥
當 Rails 專案逐漸成長之後,最令人頭痛的就是 Rails 的 boot time 實在是很久。
有些沒辦法 reload on change 的部分(例如修改 config、安裝 gem)都需要將 server 重啟,然後發現跑一次竟然要十幾秒甚至二十秒,發現 Rails developer 人生大半的時間其實都浪費在啟動時間…
除了加強硬體規格以外,比較能做的事情就是用 preloader 預先加載 Rails 進行加速,一般常用的有 spring / zeus,加上近期的 bootsnap 又更好的提昇效率了,依照多數使用者回報的數據,幾乎都能減少一半以上的時間,這個 Gem 預先把 code compile 出來的 bytecode cache 到 tmp/cache 之下再加上把 Ruby 不太有效率的 require 也 cache 所以就更快了。
不過如果用了這些工具還是想要更快的話,那就需要一些 tricks 了,這邊分享一下我的優化過程,主要會是著重於 Test + Development 模式, Production 能加速的著實有限。
如何測試啟動時間
完全測冷啟動,不吃 preloader
$ time bundle exec rake environment
# bundle exec rake environment 22.78s user 11.76s system 75% cpu 45.601 total
會吃 preloader 的啟動
$ time (bin/rails runner nil)
# Running via Spring preloader in process 60378
# ( bin/rails runner nil; ) 0.22s user 0.10s system 31% cpu 1.017 total
測試每個 gem 的加載速度,這裡我用 bumbler
測之前記得讓 bootsnap 關閉,否則會測不出來
$ bumbler
296.76 carrierwave
378.10 ethereum
393.60 devise
410.36 authy
532.06 compass-rails
546.09 newrelic_rpm
558.73 config
575.63 fog
604.26 country_select
608.17 coffee-rails
621.22 pry
711.51 sass-rails
761.82 google-authenticator-rails
887.80 stellar-sdk
948.57 twilio-ruby
1482.54 grape
2627.09 rails
測試初始化程序加載的狀況
$ bumbler --initializers
Slow requires:
279.70 newrelic_rpm.start_plugin
383.96 compass.initialize_rails
432.23 :set_clear_dependencies_hook
589.01 active_record.initialize_database
611.57 ./config/initializers/translation.rb
753.06 :load_environment_config
800.46 :load_config_initializers
3288.36 :finisher_hook
9697.74 :set_routes_reloader_hook
現在我們大概能掌握在慢在哪裡,我們可以開始著手處理了。
移除上古遺 Gem
專案大起來的時候,有些 Gem 可能是當初為了某個功能先加的,說不定後來不再使用或是有別的 Gem 取代了。
這時候就要去考古,找一下有沒有哪些 Gem 是「完全沒在用」的。
沒在用的一定要優先拔掉,這是比較好做卻也比較花時間的選項。
檢查有沒有用到較舊且有 memory leaky issue 的 Gem
可以參考 A list of gems that have memory leaks
對環境進行 Gem 分組
進行分組的用意是要明確的執行,在每個環境下只載入需要用的 Gem,避免多餘的 require 來拖慢速度。
前端相關非啟動就需要執行的 Gem, 在 Test 環境下不該被加載,應該給予 group [:production, :development]
# Styles
group :production, :development do
gem "coffee-rails", "~> 4.2"
gem "sass-rails", "~> 5.0"
gem "bootstrap-will_paginate"
gem "bootstrap-sass"
gem "bootstrap-switch-rails"
gem "bootstrap3-datetimepicker-rails", "~> 4.14.30"
gem "font-awesome-rails"
gem "jquery-ui-rails"
gem "active_link_to"
gem "rqrcode"
end
異步執行等相關任務,沒有要進行測試也不要在 Test 環境下加載
# Backgroud Jobs
group :production, :development do
gem "sidekiq"
gem "sidekiq-statistic"
gem "sidekiq-cron"
gem "sidekiq-unique-jobs"
gem "sidekiq-status"
gem "whenever"
end
只有在 Production 有用的監測服務也不要在 Development﹑Test 等其他環境下加載
group :production do
gem "newrelic_rpm"
gem "scout_apm"
end
只有開發使用的必須要自己一組
group :development do
gem "letter_opener"
gem "rubocop", require: false
# profiling
gem "bumbler", require: false
gem "rack-mini-profiler", require: false
# Dev helpers
gem "annotate"
end
分組後可以避免加載不需要的部分,讓啟動更有效率。
require: false
在 Gem file 裡面寫 require false
是指說,bundle 的情況下一定會安裝這個 Gem, 但不在 rails 啟動時直接 require 加載進來。
只在有需要的部分 require 會更有效率。
# Web Server 不需要直接 require,因為我們是單獨啟動,並不是執行在 Rails application 內
gem "puma", "~> 3.9", require: false
# sitemap 生成器,只有執行時會用到,不需要 preload
gem "sitemap_generator", require: false
# 顯示進度條的小工具,通常用在 task 執行,也只要在 task 的檔案 require 就好
gem "ruby-progressbar", require: false
# 只有語法檢測的時候會執行,其他時候 Rails app 並不會使用到
gem "rubocop", require: false
可以看到以上的範例有一個共通點,就是這些 Gem 都是單獨執行任務的,並不是隨時隨地在 Rails application layer 內需要用到的,所以可以直接 require: false
這樣一來,可以確保執行這個專案的時候一定會安裝相依套件,但卻不會在 Rails 啟動時直接加載。
別讓 Airbrake 在 development 被 require
這個一直是我認為滿煩的問題,常常會需要用到 Airbrake 來接 error,但畢竟這服務是賣 quota 的,恨不得你在 Development 模式下也用。
Development 的情況我是一定不用的,原因如下:
- 開發模式可以自己噴 Error,真的不需要你接走,謝謝喔
- Quota 要錢的
不過雖然 Airbrake 可以設定 ignore_environments = %w(development test)
,但最煩的是,他是在每個環境下都會加載的,不信你關個 server 或是退出 console 就知道了。
$ rails console
# Running via Spring preloader in process 61400
# Loading development environment (Rails 5.0.7)
# Development [1] rocket(main)> exit
# **Airbrake: closed
看到 **Airbrake: closed
了嗎?表示他是無所不在的。
如果你直接把他從 development 和其他環境下拔除
# Gemfile
group :staging, :production do
gem "airbrake", "~> 6.1"
end
那麼就會遇到
- 啟動時直接噴錯,因為 initialize/airbrake.rb 初始化加載會找不到 gem
- 有使用
Airbrake.notify(error)
的地方會爆炸
最佳解法,在不該出現的環境做一個空殼
# config/initializers/airbrake.rb
if Rails.env.staging? || Rails.env.production?
Airbrake.configure do |c|
...
...
end
Airbrake.add_filter do |notice|
...
...
end
else
module Airbrake
extend self
def notify(exception, opts = {})
end
end
end
就可以完美解決這個問題了。
Console 要用的套件不該 preload
我相信應該滿多人裝 awesome_rails_console
之類的,進 console 除了格式漂亮以外真的很慢,而且這種算是開發人員工具的也不該直接就在各個環境下加載。
我認為最好的做法是,在啟動 console 時才加載,不需要讓整個 Rails app 提早 preload
先用 require: false
group :staging, :development, :test do
# Dev helpers
gem "awesome_rails_console", require: false
end
再設定 development 環境下開啟 console 要使用 Pry
# config/environments/development.rb
config.console = Pry
建立一個 .pryrc
的檔案在 project 目錄下
# .pryrc
require "awesome_rails_console"
AwesomePrint.pry!
這樣在 Console 啟動時,會直接使用 Pry 啟動,然而 Pry 啟動前又會去讀取 .pryrc
的設定檔,如此一來,就只有在 console 啟動時會 require 了。
讓 routes 加速更快
我很遺憾,這是唯一一個目前想不到解法的部分,當專案的 routes 被越養越大,大型的路由文件在啟動上花費的時間真的很多,我目前手上最大的專案大概就有 4 秒在 reload routes,在 bumbler 的效能分析上,他是 :set_routes_reloader_hook initializer
,如果把這個問題也解掉,也許能在快上 30% ?
Recommend
-
10
Before Project - 專案啟動前 August 23, 2020 千呼萬喚 終於等到Pragmatic Programmer 20週年紀念版 如果沒聽過這本書 你大概也聽過
-
6
GTA Online 釋出官方修正,大幅改善啟動效能 看到「GTA Online load time fix released, shaves off actual minutes of...
-
6
今天因為要維護一個 11 年前完工的專案,特別把當時的 VM 還原到 Hyper-V 裡面執行,而 SQL Server 的部分我就打算直接跑在容器中。因為我的 VM 跑在 External network 網路下,所以等同於是區域網路的另一台電腦要連到我的 SQL Server on Linux 容器。照理說這...
-
8
Linux 用了二十多年,從沒認真想過 Login Shell 載入的啟動檔順序為何,我們經常會看到 .bashrc, .bash_profile, .bash_login, .profile 諸如此類的檔案,在安裝一些工具或系統的時候,也多多少少會需要設...
-
8
聯儲局啟動削減買債【匯君】 上季的非農單位勞工成本上升了8.3%,預期上升7%,再之前只升1.3%。顯示美國由於勞工不足引致工資顯著上升,但也令到有更多勞工願意工作, 讓整個就業市場有所改善。另一邊廂,小非農職位空缺也增加57.1萬,市場預期增...
-
17
以管理者權限啟動 Windows 批次檔或命令列程式-黑暗執行緒 我的電腦有個第三方常駐程式存在 Bug,Windows 睡眠喚醒後偶爾會失常,常駐程式沒提供關閉選單,只能用工作管理員砍掉再重啟。大家都知道我很受不了重複滑滑鼠敲鍵盤,每次遇到都千方百計想把它自動...
-
10
分享一組我在啟動 Windows Sandbox 時都會執行的初始設定腳本 在本機進行 Windows 應用程式的測試,最簡易的方法就是啟動 Windows Sandbox 沙盒環境,不用十秒就可以開啟一台臨時的虛擬機,關閉後所有資料都不會保留,非常方便用來驗證許多安裝作業流程...
-
10
AWS 推出加速 Lambda 啟動速度的 Lambda SnapStart 今年 AWS 的
-
2
你就是寫太多測試才會沒時間(2):自動化測試是金字塔嗎? June 20 09:55~11:46
-
10
你就是寫太多測試才會沒時間(1):證明自己的清白 June 17 16:31~18:24
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK