LoginSignup
4
3

More than 5 years have passed since last update.

ETag等の比較で更新をチェックしてリソースを取得したい

Last updated at Posted at 2016-02-03

追記:injectメソッドで書き直した

レスポンスヘッダでETagやLast-Modifiedを提供しているリソースへアクセスするとき、最終更新時と比較しつつ、定期的に更新をチェックするようなプログラムを書きたいときがあるだろう。ETagであれば、どこかに直前のETagを保持しておいて、次のリクエストヘッダに含めてGETするようなループを書けば良さそうだ、というところまでは思い当たる。しかし、「キモーイ」「変数に状態を持たせておいて、それをループの中で何度も書き換えたりするのが許されるのは小学生までだよね」「キャハハハハハ」と言われてしまったら……一体どのようにこれを実現すればいいのだろうか。

最終的にやりたいことをRubyで書くと、以下のようになる。

c = Crawler.new

c.start do |response|
  # 更新があるごとに何かする
end

これなら、startメソッドの中にイイカンジでyield的なものを書けば行けるだろうと思ったのだけど、ETagを次の処理に渡すのがなかなかうまくできなかった。FIFOっぽいなと思って、結局Queueを使って書いた。

require "logger"
require "open-uri"
require "thread"

$logger = Logger.new($stderr)
$logger.level = Logger::DEBUG

class Crawler
  def initialize(interval)
    @queue = Queue.new
    @interval = interval
  end

  def start(&block)
    t = Thread.start(block) do |block|
      while connection = @queue.shift
        response = connection.start
        block.yield(response)
        sleep @interval
        @queue << Connection.new(@interval, response.meta["etag"])
      end
    end
    @queue << Connection.new(@interval)
    t.join
  end
end

class Connection
  ENDPOINT = URI("https://example.com/index.rss")
  OPTION = {
    read_timeout: 5,
  }

  def initialize(interval, etag = "")
    @interval = interval
    @etag = etag
  end

  def start
    open(ENDPOINT, OPTION.merge("If-None-Match" => @etag))
  rescue OpenURI::HTTPError
    sleep @interval
    retry
  rescue => e
    $logger.warn("%s: %s" % [e.class, e.message])
    sleep @interval
    retry
  end
end

うーん。スレッドの処理をもう少しコンパクトに書きたいところ。ロガーの書き方も、もうちょっと考えましょう。

以下のようなコードを書けば、3秒おきにリソースにアクセスして、更新があったときに本体を取得できる。このコード自体は、起動すると、更新があるごとに新しいETagを延々とログに吐く。

c = Crawler.new(3)

c.start do |response|
  $logger.info(response.meta["etag"])
end
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3