こんにちは。
entershareで働いているじゅんといいます。
仕事では毎日Rubyのコードを書いています。
どんなプロジェクトでも、だいたいcronを回して定期実行するということが必要になってきます。
(もちろんそれがないプロジェクトもあると思いますが。笑)
定期実行というとちょっとピンと来ないかもしれませんが、例えば「毎日午前4時に、前日のサイト上の売り上げの合計計算して管理画面で見られるようにする」とか、「一時間に一回ランキングの集計をする」などです。
今回はRailsで定期実行を実装する方法を簡単にまとめていきます。
また、今回はwheneverというgemを使う実装方法をまとめます。
cronとは何か。なぜwheneverを使うのか
基本的に、定期的に何かのプログラムを自動で実行するという場合にはcronというものを使います。
ちなみに「cron」は、linux(正確にはUnix系)のOS上にある、定期実行のためのデーモンプロセスのことを指します。
このcronに「この時間にこのプログラムを実行しておいて!」「毎日のこの時間になったらこのプログラムの実行をお願い!」ということをお願いしておけば(crontabに決まった書式で書いておけば)、実際にその決まった時間に指定しておいたプログラムを実行してくれます。
Railsで定期実行をする際も、もちろんこのcronを使うことになります。
cronの設定を自分で書けるのであれば別にそれでいいのですが、その設定を書くのが面倒臭い人や、そもそもcronの設定はRailsのプロジェクトの中に書いておきたい人もいるかと思います。
そういう人向けに「Whenever」というgemがあり、それを使うことで比較的簡単に定期実行が行えます。
実際に実装開始!
早速ここから実装していきます。
と言っても非常に簡単です。
まずはGemfileにwheneverのgemを使うことを書き入れましょう。
(ここら辺はwheneverのgithubのREADME見るのが一番確実です。)
1 |
gem 'whenever', require: false |
Gemfileに上記のように書いたら、早速インストールしましょう。
1 |
bundle install |
続いて、wheneverの設定用の初期ファイルを作成するコマンドを実行します
1 |
wheneverize . |
このコマンドを実行することで、config/schedule.rbというファイルが生成されます。
このschedule.rbに、定期実行する間隔と、どのプログラムを定期実行するのかということを書いていきます。
今回は、仮に「ユーザーのlifeをmax_lifeまで回復させる」というのを1日に1回実行するというのを実装していきます。
Railsプロジェクトでは、基本的に今回でいう「ユーザーのlifeをmax_lifeまで回復させる」というような、cronを使って定期的に実行する処理のかたまりをtaskといい、それはlib/tasks以下に作成していきます。
今回は、recover_user_life.rake という名前のものを作っていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
namespace :recover_user_life do desc "全ユーザーのライフを回復させる" task recover: :environment do #ここから処理を書いていく #定期実行する際に、そのログを取っておくのは大事。ログがないと定期実行でエラーが起きても分からない。 logger = Logger.new 'log/recover_user_life.log' #全てのユーザーを取得していく #eachを使うと全てのユーザーがメモリに載せられてしまうが、find_eachにすることで1000件ずつ読み込んでメモリに展開される ユーザー数が多いならfind_eachを使った方が安全 User.find_each do |user| begin #ユーザーのライフを、max_lifeに変える。 user.update!(life: user.max_life) rescue => e #何かしらエラーが起きたら、エラーログの書き込み ただし次のユーザーの処理へは進む logger.error "user_id: #{user.id}でエラーが発生" logger.error e logger.error e.backtrace.join("\n") next end end end end |
処理の内容自体は非常に簡単です。
Userをfind_eachでまわして、それぞれのユーザーのlifeを更新するだけです。
(一応エラーがでた際にあとで確認できるようにlogを取っています。)
一行目のnamespaceはfile名と揃える必要があります。
2行目のdescは、このタスクの説明です。
3行目のtask のあとの「recover:」は自由に入れて大丈夫です。
このtaskを、まずは定期実行ではなく、通常に実行してみましょう!
その方法非常に簡単です。
まず以下のコマンドで、Rakeタスクの一覧を見ましょう。
1 |
rails -T |
するとずらずらーっと出てくると思いますが、その中の1つに、先ほど定義したタスクも以下のように出てくると思います。
1 |
rails recover_user_life:recover #全ユーザーのライフを回復させる |
これを実際に実行すると、全ユーザーのライフを回復させるtaskが実行されます!
これで終わり、、、ではなく、ここからが本題です。
これをあなたが実行するのではなく、自動で実行されるように設定していきます。
一番最初に「wheneverize .」で生成したschedule.rbを編集して以下のようにします。
1 2 3 4 5 6 |
set :output, 'log/crontab.log' set :environment, ENV['RAILS_ENV'] every 1.day do rake "recover_user_life:recover" end |
これは見たまんまですが、1日に1回recover_user_life:recoverを実行するための記述です。
これが1時間に1回の場合は1.dayを1.hourに変更すればいいですし、二時間に1回であれば2.hours、1分に1回なら1.minuteに変更すればいいです。
また、例えば毎日の早朝4時に実行するのであれば、1.dayの部分を1.day, at: '4:00 am' という風に変更してあげればOKです。
これで完了!と言いたいところですが、このschedule.rbというファイルは、単なる設定ファイルです。
これをcronに実際に反映させてあげないと定期実行は行われません。
まず、現状のcrontabに何が記述されているのかを確認しましょう。
1 |
crontab -e |
また、実際にschedule.rbを使った場合にどのように設定されるのかも、以下のコマンドであらかじめ確認できます。
1 |
whenver |
問題なさそうだったら実際にcronに反映させます!
1 |
whenever --update-crontab |
(もしcrontabの中身を消し去りたいときは、以下のコマンドで消せます)
1 |
whenever --clear-crontab |
ここでは削除せずに反映だけを行いましょう!
これだけで定期実行が行われます!かなり簡単ですよね。
今回は非常に簡単な例でしたが、基本的には定期実行はこれでどうにかなります。
が、時と場合によっては、毎時ではなく毎秒値を変更させたいというパターンもあるかと思います。
そんなときは定期実行は使えません。そのような場合の方法は別に書いたのでそっちも参考にしてみてください!