Edited at

Deleyed Jobのソースコードを読む

More than 3 years have passed since last update.


概要

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...