RSSリーダーを作ったときのことをまとめておくシリーズ。
これまでの記事はこちら。
僕が知る限り全てのRSSリーダーは登録フィードをバックグラウンドで更新し記事を追加します。RSSリーダーのユーザー側、システム側から見てみます。
ユーザー視点
- RSSリーダーにブログやニュースサイトのフィードを購読
- これまでの記事を閲覧できるようになる
- サイトが更新されたら新しい記事を閲覧可能になる
RSSリーダー視点
- ユーザーがRSSフィードを購読する
- すでに誰かが購読済みのフィードかどうかを調べる
- 誰かが購読済み→フィードの購読者にこのユーザーを加える
- 最初の購読者→フィードデータを作成し記事をクローリングする、また購読者にこのユーザーを加える
- 取得済みの記事をユーザーに見せる
- フィード配信元に更新があるかどうか定期的にクローリング
- 更新があれば新たな記事を追加する
- (更新後に見たユーザーは新しい記事を読めるようになっている)
定期的なクローリング
両者を比べてみるたときに一番違うのは、強調表示した**「フィード配信元に更新があるかどうか定期的にクローリング」**というところです。もっと言い換えれば、更新があるかどうか見に行くという行為をユーザーの代わりに行ってくれるのがRSSリーダーの価値であると言えます。
そして、システムからすればもっとも負荷の高い部分のうちの1つとも言えます(もう一つはクロールした記事のデータ量。日に日に増えていくので、、、)。
ではこのクローリングをどのように実施したかを紹介していきます。
Rails & Resque
クローリングはユーザーの操作がないときでも定期的に動かす必要があります。したがってアプリケーションサーバーやWebサーバーは関係ありません。もっとも簡単に実行するには、定期的にスクリプトが動いてデータベースを更新するようなものでもできてしまいます。
とはいえ、単なるジョブ用スクリプトを使うとアプリケーションと同じモデルを利用するのが困難でスクリプト自体が大変複雑になることは間違いないので、もうちょっとマシな方法を使います。
RSSリーダーはRuby on Railsで作り、バックグラウンドジョブにはResqueを使用しました。
- Ruby on Rails
- Resque
Resqueに関してはiQONを運営するVASILYさんの記事が詳しかったのでこちらを参考にいろいろ実装しました。超助かりました。
Resqueのバックグラウンドジョブ用のワーカーは定期的にジョブキューを監視しつづけてくれます。暇なワーカーがいる限り、ジョブキューにジョブを放り投げるとすぐにジョブを実行してくれるようになっています。つまりワーカーを多数用意しておけば、がんがんジョブを回すことができますね。
RSSフィードの定期チェックのためには「あるRSSフィードを見にいって、更新があれば記事を追加する」というジョブを全ての記事分ジョブキューに登録すればよい、ということになります。
定期的にジョブを発行する
ジョブを処理する側はResqueを使って実装できました。ではジョブはどのように発行すればいいでしょうか?
これにはcron
のようにある時間毎に起動するプログラムを用いることにしました。
tomykaira/clockwork [https://github.com/tomykaira/clockwork:image:large]
このclockwork
というgemを使うと、「◯◯分毎に△△する」というようなデーモンをRubyで書くことができます。実際には次のような手段を取りました。
- clockworkを使い1分ごとにRSSリーダーの特定のURL(例:/feed/update)にアクセス
- Rails側で上のURLに対応したコントローラのアクションを作っておく
- 対応アクションで更新ジョブをジョブキューに登録する
これで定期的にジョブを発行するという流れができました。
この方法だとジョブ発行アクションが1時間に60回呼ばれることになります。1回のアクション毎に1個のフィード更新ジョブを走らせていたのでは、ユーザーが期待するフィード更新頻度を保てません。
ユーザー視点で考えると元サイトが更新されたらすぐにRSSリーダーに反映されて欲しいはずなので、せいぜい1時間に1回はすべてのフィードを更新するようにしておくべきです。
これは単純計算すると、RSSリーダーのシステム内に存在するフィード数の60分の1を1分で更新しなければいけません。1分毎に呼ばれる更新アクションでは、フィードの更新時刻が古いものから60分の1個を取り出して、それを更新するジョブを作りジョブキューに投げるということを行います。
Resqueワーカーの数が十分で、データベースがボトルネックにならないのであれば、これでユーザーが必要としているRSSリーダーの価値の部分を実装できたことになります。
まとめ
- RSSリーダーはバックグラウンドのフィード更新処理が肝
- ジョブキューを監視するワーカーをたくさん動かしておく
- ユーザーが期待する頻度で更新ジョブをキューに登録する
でも、さっきしれっと書いた十分な数のワーカーを動かしたりボトルネックにならないデータベースを用意するほうが大変だったりする。