celery

celery とは
==========
[セロリ](http://celery.readthedocs.org/)。python job queue 処理のフレームワーク。worker daemon を待機させて async 処理の仕組みを作ったり、beat daemon を起動して定時バッチ処理を組んだりする。ドキュメントはよく整備されている。

* RDBよりも効率よく
* backend は rabbitmq cluster や redis cluster を使う。RDB 使えるけど使わないのが [best practice](https://denibertovic.com/posts/celery-best-practices/)
* job の流量制御は変更しやすいようにと考えられている
* worker daemon 起動。一台に複数起動してもよし。クラスター分散させて起動してもよし。
* job 依頼側と受け手側の両方が python で共通ライブラリ使うとかだと、どんぴしゃ

処理フロー的には AWS lambda や IBM bluemix OpenWhisk みたいな処理部分に、タスクスケジューラがついたものと考えても近い。API 定義とか気にせず処理分割を進められるのはありがたい。

worker
======
async job を使いたい場合は、worker daemon を起動させる。

`celery -A proj worker` でデーモン起動させるのだけど、proj の指定方法がわかりにくい！ 

ざっくりルールはこんな感じ。

* カレントディレクトリと module 検索パスを検査対象として、Celery instance を探す
* `proj` の文字列中に `:` があると `modname:varname` が Celery instance に違いない
* `proj.app` がモジュールでなければ Celery instance に違いない（おいおい） 
* `proj.celery` がモジュールでなければ Celery instance に違いない（うーむ）
* `proj.celery` がモジュールなら
  * `proj.celery.app` がモジュールでなければ Celery instance に違いない（おいおい） 
  * `proj.celery.celery` がモジュールでなければ Celery instance に違いない（うーむ）
  * `proj.celery` 内の変数に Celery のサブクラスインスタンスが無いか調べる
* `proj` 内の変数に Celery のサブクラスインスタンスが無いか調べる

つまり例示すると…

* カレントディレクトリにある `somename.py` ファイルに
Celery instance の変数 x が存在する場合
  * `celery -A somename:x worker` で起動する。あるいは
  * `celery -A somename worker` でも起動できる。
* カレントディレクトリから見て `somename/__init__.py` ファイルに
Celery instance の変数 x が存在すれば
  * `celery -A somename worker:x` で起動する。あるいは
  * `celery -A somename worker` でも起動できる。
* カレントディレクトリから見て `somename/__init__.py` が存在し、
`somename/celery.py` ファイルに Celery instance の変数 x が存在すれば
  * `celery -A somename.celery:x worker` で起動する。あるいは
  * `celery -A somename.celery worker` で起動する。あるいは
  * `celery -A somename worker` で起動する。
  * `from __future__ import absolute_import` を使うべし（python 2）
* カレントディレクトリから見て `somename/__init__.py` が存在し、
`somename/subname.py` ファイルに Celery instance の変数 x が存在すれば
  * `celery -A somename.subname:x worker` で起動する。あるいは
  * `celery -A somename.subname worker` で起動する。
* モジュール検索パスの `somename` のモジュールに `app` 変数が存在すれば、それを Celery instance だと思い込む。
  * `celery -A somename:app worker` で起動する。
  * `celery -A somename worker` で起動する。
* モジュール検索パスの `somename` のモジュールに Celery instance の変数 x が存在すれば
  * `celery -A somename:x worker` で起動する。
  * `celery -A somename worker` で起動する。
* モジュール検索パスの `somename.celery` がロードできて、その中に Celery instance の変数 x が存在すれば
  * `celery -A somename.celery:x worker` で起動する
  * `celery -A somename.celery worker` で起動する。
  * `celery -A somename worker` で起動する。
  * `from __future__ import absolute_import` を使うべし（python 2）

注意深く見ていると、パターンによって app の表示が少し変わる。

```
celery -A somename worker
...
- ** ---------- [config]
- ** ---------- .> app:         __main__:0x106a950
```

```
celery -A somename worker
...
- ** ---------- [config]
- ** ---------- .> app:         somename:0x1585410
```

ややこしい元凶は `proj.app` と `proj.celery` という特殊ルール。

極端にややこしいケースは [Flask のドキュメント](http://flask.pocoo.org/docs/0.10/patterns/celery/) で説明がないところ。Flask は一般的には Flask instance な `app` 変数を作る。ところが celery も `app` 変数が見つかればCelery instance に違いないと思い込むので、食い違いが起こる。

回避策は例えばこのようになる：

* Flask instance の変数名を `app` 以外にする
* `:` 付きの文字列で指定する `celery -A your_application:celery worker`

beat
====
定時 batch を組みたいときは beat な daemon を起動させる。

```
celery -A somename beat
```

worker daemon とは別に起動させる。こちらは基本的にはスケールアウトしないので、注意して使わないといけない。

スケジュールの設定もプログラムには埋め込めず、基本設定ファイルに列挙することになるのも注意。

ところで
======
job queue とセロリの関係がよくわからない…そういうイメージなの…？

追記 : 今なら kafka + [faust](https://faust.readthedocs.io/en/latest/) という選択肢もありますぞ。