概要
Rubyで非同期処理を行うためのgemのdelayed_jobのソースコードを読む中で学んだことをまとめていきます(週1回ずつ更新予定)。
https://github.com/collectiveidea/delayed_job
delayed_jobについて
- delayed jobについて
以下のQiitaの記事が凄くわかりやすいです。
http://qiita.com/azusanakano/items/1d2629763f35b5466286
- capistranoでのdelayed jobの設定
デプロイするときの設定。
https://github.com/collectiveidea/delayed_job/wiki/Delayed-Job-tasks-for-Capistrano-3
内容
第1回: SignalとThread
ソースコード
DelayedJobは、アプリケーションと別のプロセスを走らせて、そのプロセスが一部の処理を担当することで、非同期処理を実現する。そのプロセスを走らせるコマンドが以下。
rake jobs:work
第1回はここから順にコードを読んでいくことに。
このrakeタスクは以下に実装されている。
https://github.com/collectiveidea/delayed_job/blob/master/lib/delayed/tasks.rb#L7-L10
namespace :jobs do
desc 'Start a delayed_job worker.'
task :work => :environment_options do
Delayed::Worker.new(@worker_options).start
end
Workerクラスがプロセスの起動を担っているとわかる。Workerクラスのstartメソッドは以下に実装されている。
https://github.com/collectiveidea/delayed_job/blob/master/lib/delayed/worker.rb#L150-L190
module Delayed
class Worker
def start # rubocop:disable CyclomaticComplexity, PerceivedComplexity
trap('TERM') do
Thread.new { say 'Exiting...' }
stop
raise SignalException, 'TERM' if self.class.raise_signal_exceptions
end
# ...
end
end
end
いきなり'trap'とか'Thread'とか出てきて何も分からなかったので、まずはこの辺のことをググって勉強。
Signalクラス
まず最初の'tarp'は、'Signal'クラスのメソッド。
シグナルは、例えばプログラムの実行中にCtrl-Cを押すとプログラムを強制終了することができますが、そういったプログラムとは別の外部からの入力(この例だとCtrl-C)を受け付ける。
Signalクラスを使ったサンプルコード。
While true
puts 'Chris'
sleep(2) # 2秒ごとに'Chris'と表示される
Signal.trap(:INT){
puts "Stop" # Ctrl-Cを押すと'Stop'と表示される
exit
}
end
これを実行すると、
Chris # 無限ループでChrisと表示される
Chris
...
Chris # ここでCtrl-Cを押す
Stop # プログラムが終了する
こんな挙動になる。delayed_jobでworkerを動かしているときもCtrl-Cでプロセスを終了できるが、これはこういった具合に実装されている。
Threadクラス
次にThreadクラスのインスタンスを生成している。スレッドはメモリを共有するが、実行を並列化するための仕組み。
以下サンプルコード。
def thread_sample
threads = []
3.times do |i|
threads << Thread.new do
sleep(3-i)
puts "#{i+1}番目の処理"
end
end
end
やっていることとしては、1回目の処理は3秒経ってから実行され、2回目の処理は2秒経ってから実行され、3回目の処理は1秒経ってから実行されるというもの。実行結果は以下。
3番目の処理
2番目の処理
1番目の処理
非同期に実行されていることが分かると思います。
この辺の非同期な実行の部分にdelayed_jobの実装の本質があるのかなと思ったところで第1回は終了です。
分かったことまとめ
- delayed_jobのプロセスはWorkerクラスに実装されている
- Signalクラスを使って、workerを終了させられる
- Threadクラスによって並列処理を行っている(かも) > 次回以降
参考にしたページ
シグナル
http://docs.ruby-lang.org/ja/2.0.0/class/Signal.html
http://www.oki-osk.jp/esc/linux/signal.html
http://dark7110.blog.fc2.com/blog-entry-17.html
http://unageanu.hatenablog.com/entry/20080127/1201423758
スレッド
http://docs.ruby-lang.org/ja/2.1.0/class/Thread.html
http://shirusu-ni-tarazu.hatenablog.jp/entry/2013/07/02/042448
https://recompile.net/posts/active_record_in_a_multi-threaded.html
第2回
To Be Continued...