1. やりたいこと
作成したElixirのライブラリを呼び出すジョブを定期的に叩きたいな、と思いました。
- cronを使う。 -> でもsystemdとかになったらTime Unit使わなきゃいけないしなんとなくわかりづらい。。
- Jenkinsなどのジョブスケジューラコンテナを立てたりする。 -> 一個だけしか動かしたくないし、牛刀か。。
ということで、Javaのquartzみたいなライブラリないのかな、とあさっていたら見つけました。
2. 作者の人の資料と内部実装について
以下でご紹介されているようです。
pointは2つあるようで、
- timeスケジューラのエイリアスを定義しているが、List.foldr等で変換処理を行っているよ
- timer処理を
Process
ライブラリを利用して管理しているよ
と言ったとこでしょうか。2. についてはMacro
という表記がスライド中にあるのですが、まだ私はキャッチアップが終わってないのでこれから追いかけてみようと思います。楽しそうですね^^
1. についてのコード
defmodule Quantum.Translator do
@moduledoc false
@days ~w{sun mon tue wed thu fri sat}
@months ~w{jan feb mar apr may jun jul aug sep oct nov dec}
# Replaces all occurrences of abbreviated day and month names by their index
def translate(s) do
{s, _} = List.foldl @days, {s, 0}, fn n, acc -> do_translate acc, n end
{s, _} = List.foldl @months, {s, 1}, fn n, acc -> do_translate acc, n end
s
end
defp do_translate({s, i}, n) do
{String.replace(s, n, "#{i}"), i + 1}
end
end
accumulator
のnの値を1個ずつincrementすることで、@days
や@months
を数字に変換してますね。
2. についてのコード
defmodule Quantum.Timer do
@moduledoc false
def timezone_function do
case Application.get_env(:quantum, :timezone, :utc) do
:utc ->
&:calendar.now_to_universal_time/1
:local ->
&:calendar.now_to_local_time/1
timezone ->
raise "Unsupported timezone: #{timezone}"
end
end
def tick do
{d, {h, m, s}} = timezone_function.(:os.timestamp)
Process.send_after(self, :tick, (60 - s) * 1000)
{d, h, m}
end
end
ここがちょっとまだ?な状態です。&
演算子ってなんだっけ。。
send_after
についてはelixir Process apiを見てみます^^
3. 使ってみました
以下のリポジトリで使ってみました。
3-1. mix.exsを編集
quantumを追加しました。
defp deps do
[{:mix_test_watch, "~> 0.2", only: :dev},
{:tentacat, "~> 0.2"},
{:riak, "~> 1.0"},
{:poison, "~> 2.0"},
{:quantum, ">= 1.6.1"},
{:factory_girl_elixir, "~> 0.1.1"},
{:credo, "~> 0.3", only: [:dev, :test]}]
end
applicationにも追加が必要なようです。
defp applications(_all), do: [:logger, :tentacat, :riak, :quantum]
3-2. quantum用のconigを作成
Mix
のconfig
ファイル設定で、スケジュールと実行させるメソッドを定義づけることができるようです。
READMEを参考にしつつ、毎分起動するジョブを定義しました。
config :quantum, cron: [
# Every minute
"* * * * *": {GithubStalking, :say_hello}
]
呼ばれている関数の実態は以下のとおりです。
def say_hello() do
Logger.info("Hello! World!")
end
3-3. 常駐プロセス型のメソッドを用意する
ちょっとカッコよく書きましたが、お試しなので、無限ループするメソッドをちょろっと用意して試すことにしました。
毎秒インクリメントされた数字を表示するようにします。
def my_loop(i) do
:timer.sleep(1000)
Logger.info(i)
my_loop(x+1)
end
3-4. mix runで実行する
mix run -e 'GithubStalking.my_loop(0)'
もちろん、この時点でdetatchしても良さそうですね。
3-5. 出力の様子
06:17:55.557 [info] 1893
06:17:56.562 [info] 1894
06:17:57.564 [info] 1895
06:17:58.566 [info] 1896
06:17:59.569 [info] 1897
06:18:00.236 [info] Hello! World!
06:18:00.573 [info] 1898
と、上記のような感じで毎分出力され続けます。
常時実行しているプロセスに対して割り込みのような形で起動しているのか、などなどはきちんとソースを読んで確認してみようと思います^^
4. 所感
- 設定自体は簡単なので気軽にお試しができそう。
- ソースも小さいし、勉強がてら読むのには良さそう。
- エラーハンドリングがしづらい。。ジョブが動いた時に、ジョブの内部でエラーが起きた時に出力されるエラー内容がきちんと表示されないような。。(ソース読もう!)
5. 参考
本日は以上となります。