タスクリストの管理にredisを使ってみる
本題だけ知りたい方はこのブロックを読み飛ばしてください
私はほぼ毎日某サイトからスクレイピングしてデータを取得し、DBに突っ込んでいます。多い日には300ページ以上から取得することもあります。
スクレイピングする時のマナーとして「無闇にアクセスしない」ということを意識してるのでリクエストの間隔を2秒程度は空けることにしています。
つまり300ページから取得する場合少なくとも300×2=600秒=10分かかります。
最初に300ページのURLを並べたタスクリストを作り、それを順番に回します。
しかし途中で通信が切れたりURLが間違えていた場合エラーになり、やり直すことになります。
再度処理を行うときにはタスクリストの成功した項目を省いて未取得のURLのみを処理することになります。
以前はDBにLastUpdateという項目があったのでそれを目安に再取得していたのですが、DBの設計を変更した結果同じ方法は使えなくなりました。
これを解決する手段、つまりタスクリストの管理を行う方法を考えてみました。
別にタスクリスト専用のtableを作ってDBで管理する
タスクリスト専用のファイルを使って読み書きする
こんなところでしょうか。
しかしタスクリストは使い捨てです。DBでもファイルでも破棄するのが手間です。というか一旦DBに保存したものを削除するのは怖いです。
そこで名前は聞いていたけど使ったことのないredisを試してみることにしました。
やりたいこと
- fetchWithUrl()という関数があるとします。引数にURLを渡すとスクレイピングしてDB保存する関数です
- urlListというタスクリストがあるとします。複数のURLが入っている配列です。
- urlListをloopして fetchWithUrl()に渡していきます。
- fetchWithUrl()が成功したら urlListからurlを削除します。
- この一連のプロセスが異常終了した時の為に urlListを半永続化させたい
この 半永続化のためにredisを使います。
セットアップ
例えばmacでhomebrewが入ってるならワンライナーでインストールできます。
brew install redis
私はnodejsを利用していますが、moduleのインストールも1行です。
npm install redis
redisの型
redisはKey-Value Store、つまりkey: value
という形で値を設定できます。
そのvalue部分は以下の型が使えます。
動作確認
今回はタスクリストとしてセット型というヤツを使ってみようと思います。手順は以下の通り。
- URL一覧をsetに追加する
- setのコピーを取得してloopする
- 処理が成功ならsetからitemを削除する
簡単なtestを以下に記します。(nodejsでcoffee-scriptです)
※非同期処理の為にasyncを使用しています
async_ = require 'async'
describe "redis", ->
it "test",(done) ->
key = 'testSet'
redis = require('redis').createClient()
redis.sadd key,['a','b','c'],(err)->
throw err if err
redis.sinter key,(err,arr)->
throw err if err
console.log arr
async_.eachSeries arr, (item, next) ->
redis.srem key,item,(err)->
redis.sinter key,(err,newArr)->
throw err if err
console.log newArr
next()
, (err)->
done()
# [ 'a', 'b', 'c' ]
# [ 'b', 'c' ]
# [ 'c' ]
# []
ネストがひどいのでasync.waterfallで描き直してみます。
async_ = require 'async'
describe "redis", ->
it "test",(done) ->
key = 'testSet'
redis = require('redis').createClient()
async_.waterfall [
(next)-> redis.sadd key,['a','b','c'],(err,res)->
assert.isNull(err)
next()
(next)-> redis.sinter key,(err,arr)->
assert.isNull(err)
console.log arr
next(null,arr)
(arr,next)-> async_.eachSeries arr, (item, next_) ->
redis.srem key,item,(err)->
redis.sinter key,(err,newArr)->
assert.isNull(err)
console.log newArr
next_()
,(err)->next()
],(err)->done()
# [ 'a', 'b', 'c' ]
# [ 'b', 'c' ]
# [ 'c' ]
# []
上記では以下のredisメソッドを使用しています。
sadd(key, member,callback)
keyにmemberを追加する(型はset)
sinter(key,callback)
keyを指定してsetを取得します
srem(key,member,callback)
keyとmemberを指定してsetの要素を削除します
最初にsaddで['a','b','c']を追加(これがタスクリストになる)、
それを取得してasync_.eachSeriesで回す。(タスクリストからアイテムを渡し個別処理を行う)
個別処理が無事に終わればsremでアイテムを削除する、というイメージです。
次に itemがbだったら削除しないという感じに書き換えてみます。個別処理に失敗したというイメージです。
describe "redis", ->
it "test",(done) ->
key = 'testSet'
redis = require('redis').createClient()
async_.waterfall [
(next)-> redis.sadd key,['a','b','c'],(err,res)->
assert.isNull(err)
next()
(next)-> redis.sinter key,(err,arr)->
assert.isNull(err)
console.log arr
next(null,arr)
(arr,next)-> async_.eachSeries arr, (item, next_) ->
# ここを修正
if item isnt 'b'
redis.srem key,item,(err)->
redis.sinter key,(err,newArr)->
assert.isNull(err)
console.log newArr
next_()
else
next_()
,(err)->next()
],(err)->done()
# [ 'a', 'b', 'c' ]
# [ 'b', 'c' ]
# [ 'b' ]
bの場合sremが行われないので、タスクリストに残りました。
上記のコードを実行して一旦プロセスは終了しているので、タスクリストが変数だとしたら消えていますが、redisなので残っているはずです。
上記のコードからsaddの部分とsremの部分を外してもう一回動かしてみます。
describe "redis", ->
it "test",(done) ->
key = 'testSet'
redis = require('redis').createClient()
async_.waterfall [
# (next)-> redis.sadd key,['a','b','c'],(err,res)->
# assert.isNull(err)
# next()
(next)-> redis.sinter key,(err,arr)->
assert.isNull(err)
console.log arr
next(null,arr)
# (arr,next)-> async_.eachSeries arr, (item, next_) ->
# if item isnt 'b'
# redis.srem key,item,(err)->
# redis.sinter key,(err,newArr)->
# assert.isNull(err)
# console.log newArr
# next_()
# else
# next_()
# ,(err)->next()
],(err)->done()
# [ 'b' ]
bはちゃんと残っていました。
この要領でタスクリストに使えそうです。
まとめ
redisはtwitterでも使われているというニュースを聞いて久しいですが、使いどころがわからなくて今まで使ったことがありませんでした。
今回は[プロセスが終了しても維持できる変数]くらいの感じで使ってみたのですが意外に便利です。
特にredis = require('redis').createClient()
の1行でセットアップ完了というのが気に入りました。
しかし、もしかしたらredisよりも新しくて便利な技術があるかもしれませんので、ご存知の方は教えて頂けたら嬉しいです。
参考サイト
NodeRedis/node_redis: redis client for node
Author And Source
この問題について(タスクリストの管理にredisを使ってみる), 我々は、より多くの情報をここで見つけました https://qiita.com/__mick/items/7e5b5103000f826a932a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .