Railsで外部APIを毎秒&永続的に値を取得し、DBに格納する
まえおき
やりたいことはタイトル通りなのだが、実際にやってみると当初考えていたより苦戦したので、その時の作業メモをまとめておく。
環境
Rails: 6.1.0
Ruby: 2.7.2
Faraday: 0.17.4
結論
ruby のloop + sleepを用いたメソッドを作成し、rails runner
で実行する。
参考にした記事: 【Ruby】faradayを利用した外部API連携クラスの作成手順
やり方
1. メソッド作成
lib/hoge/fuga.rb
を作成。中身は以下の通り。
class Hoge::Fuga < ApplicationRecord
INTERVAL = 1
URL = '外部APIのUTL'
def self.get
loop do
sleep(INTERVAL)
response = Faraday.get(URL)
res = JSON.parse(response.body)
res['created_at'] = Time.current
Fuga.insert_all([res])
end
end
end
Tips
-
loop
内でsleep
を使うことで、引数に与えた秒数の間処理を一時停止させる。今回は毎秒にしたかったので引数(INTERVAL
) を1にしている。環境変数をセットし、実行時に秒間を調整できるようにしてもいいかも。 - 今回は取得した値をほぼそっくりそのままDBへ格納したかった。そのため、いちいち各カラムに値を入れ込むのが面倒だったので、ハッシュ形式のまま入れ込めるinsert_allを使用した。
- ちなみに
insert_all
ではcreated_at
やupdate_at
を自分で入れ込まなければならないので、res['created_at'] = Time.current
を記述している。
- ちなみに
- 外部APIとのやりとりはFaradayを使うと便利。ドキュメントも充実してておすすめ。
- エラー処理などはここでは記述してないので、別途記述が必要。
2. configにパスを追記
libはRailsで自動的にロードされないため、config/application.rbに以下を追記。
config.autoload_paths += %W(#{config.root}/lib)
3. runnerで実行
bin/rails runner Hoge::Fuga.get
で実行
採用しなかったこと
whenever
1.秒単位の設定ができなかったため採用しなかった。1秒毎にAPIを叩くrunnerを作成しそれを1分毎に実行する、とすれば一応やりたいことは実現できるが、なんとなく微妙だなと思ってやめた。
似たようなことをwheneverでするにはどうすればいいかという質問がstackoverflowに投稿されていたが、その中では「wheneverではできないけど、シェルスクリプトを使えばいけるぜ」と回答されていた。
2. 1秒毎に実行されるrake task をDaemonで実行する
以下の記事を参考にやってみようと思ったが、紹介されているdaemon-spawngemというgemがほとんど更新されていなかったため採用しなかった。
まとめ
とりあえず今回のやり方でタイトル通りのことはできるようになった。ただしエラー処理は考慮してないため、それは別途書く必要があると思う。また本番環境では無理にrunnerを動かすより、awsのlamdaなどで定期実行させた方がいいかもしれない。もしやってる方がいたら、ぜひコメントで教えてください。
またもっといいやり方があるよとか、うちではこういう風にしてるよとかあれば是非コメントよろしくお願いします。
参考資料
Discussion