コマンド「QUEUE=resque_sample rake environment resque:work」を読み解く


Railsのコントローラで定義したメソッドの中で、長い処理は非同期で行いたいときはありませんか？例えばユーザー登録のメール送信などは、コントローラのメソッドの中でメールが送信されるまで処理を止めるのではなく、非同期にして先に「登録が完了しました。」とページを表示させてあげる。こういうのをrailsでやるのにresqueというgemが役立ちます。

このgemのチュートリアルである下記の記事を試してみました。

[Hello World Resque (Railsにresqueを導入する) | hello-world.jp.net](http://blog.hello-world.jp.net/ruby/895/)

今回は定義したキューを実行させるための下記のコマンドを掘り下げて見ていこうと思います。

`QUEUE=resque_sample rake environment resque:work`

ちなみに次のように書いても実行できます。QUEUEを後ろに回しただけです。

`rake environment resque:work QUEUE=resque_sample`

### QUEUEは環境変数で、どの種類のタスクを実行するのか決めている

`QUEUE=resque_sample`は`QUEUE`という環境変数を定義して、そこにresque_sampleという文字列を格納しています。その後にrakeでタスクを走らして、そのタスク内でこの環境変数の中身を使っているわけです。

参考：[Linux - シェル変数と環境変数の違いをコマンドラインで確認する - Qiita](http://qiita.com/kure/items/f76d8242b97280a247a1)

簡単な例をあげます。 `ruby_script_test`とうファイルを作り、次のとおりに記述します。その後`chmod 755 ruby_script_test`をコマンドラインから実行して、このファイルに実行権限を与えてあげましょう。

```ruby
#!/usr/bin/env ruby
puts ENV['SAY']
```

そしてコンソールで次のように打ってください。環境変数の中身が出力されましたね。

```shell-session
$ SAY=hello
$ ./ruby_script_test
hello
```

rubyで記述されたファイルをrubyコマンドなしで実行できる仕組みは下記が参考になります。簡潔にまとめると、ファイルの拡張子があろうがなかろうか、1行のめシバンとよばれる`#!~`の記述でどのコマンドで実行すればいいかをファイル自体に記述しているからrubyコマンドなしで実行できるわけです。

[ruby - rubyで作ったプログラムをターミナルからコマンドで実行したい。 - スタック・オーバーフロー](http://ja.stackoverflow.com/questions/1833/ruby%E3%81%A7%E4%BD%9C%E3%81%A3%E3%81%9F%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%82%92%E3%82%BF%E3%83%BC%E3%83%9F%E3%83%8A%E3%83%AB%E3%81%8B%E3%82%89%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%9F%E3%81%84)

ちなみに、`QUEUE`に`resque_sample`を指定していますが、これは`app/workers/hello.rb`に`@queue = :resque_sample`と記述したからです。`@queue = :another_task`とした場合は、`QUEUE=another_task rake environment resque:work`と実行しなければなりません。

キューというのはタスクをやることリストとして管理するためのもので、それをいくつか名前をつけて用意できます。今回だと、`resque_sample`や`another_task`という2種類のキューを試したわけです。`QUEUE= resque_sample rake environment resque:work`とは`resque_sample`というキューに登録されたタスクを消費し始めるとコマンドです。「消費し始める」が重要ですよ。このコマンドを実行していなくても、今回のチュートリアルの/helo/worldというページにアクセスすれば`Resque.enqueue`が実行されてキューにどんどん溜まっていきます。その後に`QUEUE= resque_sample rake environment resque:work`を実行すれば、溜まったタスクがどんどん実行されていくので、ページにアクセスしなくても実行されているように見えます。

実際にはRedisというKVSのDBに、「resque:queue:ワーカ名」の配列としてどんどんジョブが格納されていきます。そして実行が終わるたびに配列から要素が取り出されていきます。詳しくは下記を参考。

[Resqueで色々やって、Redisに何が格納されているのか調べてみた - kitak.blog](http://kitak.hatenablog.jp/entry/2013/08/11/232218)

### environmentはモデルを読み込むために使う

rakeコマンドにenvironmentという引数を渡しています。これを渡すことによってタスク内でActive Recordのモデルなどを使用できるようになります。下記は`rake user:regist`で実行できるタスクです。中でUserモデルを使用しています。これはtask関数でenvrionmentを渡しているからです。これを削除したらUserモデルは使えなくなってしまいます。

```ruby
# lib/task/user.rake
namespace :user do
  desc "ユーザーを登録するタスクです。"
  task regist: :environment do
    User.create(name: '太郎')
  end
end
```

しかし、この記述がなくてもUserモデルをタスク内で使える方法があります。それが最初にあげたコマンドの`rake environment`です。コマンドラインから`environment`を渡しているのでモデルなどが使用できるようになるわけです。

参考：[[rails]Rakeタスクでのtask定義の「:environment」引数について | アプレンティス プラクティス](http://blog.takapra.com/2013/12/rake-environment-args/)

### resque:workというタスクはどこで作られる？

`lib/tasks/resque.rake`というファイルを作成して、`require 'resque/tasks'`という記述を書きましたよね。`resque/tasks`を読み込むことによって自動で作られるみたいです。[githubのlib/resuq/tasks.rb](https://github.com/resque/resque/blob/master/lib/resque/tasks.rb)を見てみましょう。コメントにもこれをrequireしたらresqueタスクが作られると書いてありますよね。それと`namespace :resque`の中に`task :work`が定義されていることも確認できます。

```ruby
# require 'resque/tasks'
# will give you the resque tasks
require 'resque/cli'

namespace :resque do
  task :setup

  desc "Start a Resque worker"
  task :work => [ :preload, :setup ] do
    warn "DEPRECATION WARNING: Rake tasks are deprecated. Use `resque work` instead"

    opts = [
      "-q", ENV['QUEUES'] || ENV['QUEUE'] || "*",
      "-t", ENV['RESQUE_TERM_TIMEOUT'] || 4.0,
      "-d", !ENV['BACKGROUND'].nil?,
      "-p", ENV['PIDFILE'],
      "-i", ENV['INTERVAL'] || 5
    ]

    Resque::CLI.new([], opts).invoke(:work)
  end
# 略
end
```