AWS上で動くRailsアプリのページビューを保存する方法について書いていきます。
今回は基本的なことのみ書きますが、例えばブログのデイリーPVランキング、個別ページのPV数の推移グラフの作成などに応用出来ます。
RedisをAWSに追加
AWSには、 ElastiCache というRedisやMemcachedなどのNoSQL専用のサービスがあります。
新規Redisインスタンスの作成手順は簡単なので、他のサイトの記事を参考にしてRedisを追加してみてください。
僕は下のサイトを参考にしました
Using AWS ElastiCache for Redis With AWS OpsWorks
RedisをAWSに追加して、インスタンスの詳細画面に移動してください。
RailsでRedisを使う際に、画面のPortとEndpointを使用するので、Railsの環境変数に追加しておきましょう。
ENV["REDIS"] = "xxxx.xxxx.chache.amazonaws.com:6379" # Endpoint + Port
AWSで追加したRedisは、AWSのRailsインスタンスからしか接続できません。従って、ローカルからはElastiCacheのRedisに接続することが出来ないので、もしAWSではなくローカルでRedisを使いたい場合は、以下のようにRedisをインストール、設定してください。
1. Redisのインストール (Mac)
$ brew install redis
$ redis-server
2. Railsの環境変数に追加
ENV["REDIS"] = "localhost:6379"
RailsでRedisを使うための設定
RailsでRedisを扱うためにgemを追加します
gem 'redis'
$ bundle install
initializerフォルダに、Redisの初期設定を追加します。
require 'redis'
uri = URI.parse(ENV["REDIS"])
REDIS = Redis.new(host: uri.host, port: uri.port)
これで、REDISという変数をRailsから呼び出すことによって、Redisを使えるようになりました。
RedisにPV数を保存
例えば、CMSアプリケーション(ブログなど)で使う場合を考えます。
Postモデルがあって、PostsControllerからページの操作をするとします。
PostControllerのshowメソッドで、個別ページを表示するなら、以下のコードを追加します。
def show
@post = Post.find(params[:id])
...
REDIS.incr "posts/daily/#{Date.today.to_s}/#{@post.id}"
end
REDIS.incrは、数値に1を足して保存するメソッドです。
例えば、2014年4月1日に、id=100の記事が初めて読まれたとすると、
Redisの管理するハッシュのキー: posts/daily/2014-04-01/100
に「1 (= 0 + 1)」が保存されます。
もう一度読まれた場合は、
Redisの管理するハッシュのキー: posts/daily/2014-04-01/100
に「2 (= 1 + 1)」が保存されます。
さらにもう一度読まれた場合には、
Redisの管理するハッシュのキー: posts/daily/2014-04-01/100
に「3 (= 2 + 1)」が保存されます。
このように、ページビュー毎にID別のPV数がRedisに保存されます。
RedisからPV数を取得
REDIS.get "posts/daily/#{Date.today.to_s}/#{@post.id}"
# => 今日のPV数
REDIS.get "posts/daily/#{Date.yesterday.to_s}/#{@post.id}"
# => 昨日のPV数
例えば、全ての記事に関してPV数を取得すれば、毎日の人気記事ランキングを作成出来ます。
Ex.)
@posts = Post.all
@daily_pageviews = Hash.new
today = Date.today.to_s
# 個別記事のPV数を取り出す
@posts.each do |post|
@daily_pageviews[@post.id] = REDIS.get "posts/daily/#{today}/#{post.id}"
end
# PV数のソーティング
@daily_pageviews.sort_by{|k, v| v}
#上位10個の記事を返す
@top10_pages = @daily_pageviews[0..10]
(追記) より良い方法
Redisにはソート済みセットという型が用意されています。もし月間PVランキングみたいなものを実装したいのであれば、この型を使うと自動的にランキングが出力されるので便利です。
ソート済みセットの関数「zincrby」は、「キー・数値・メンバー」を引数とし、あるメンバーにキーが存在すれば数値分だけ増やし、キーが存在しなければ数値をセットします。
例えば、「2015/1/1の記事」というメンバーに「id=10の記事」というキーがあって、「PV数が100件」といった数値があると考えると、PV数のランキングは以下のように実装できます。
def show()
@post = Post.find(params[:id])
...
# ex.) REDIS.zincrby "posts/daily/2015-01-01", "1", "10"
# 2015年1月1日にid=10の記事の総PV数を1増やす
REDIS.zincrby "posts/daily/#{Date.today.to_s}", 1, "#{@post.id}"
end
zrevrange関数は、メンバーの降順にソートされた数値の中から、指定された範囲の数値を持つキーを取得します。昇順の場合はzrange関数を使います。
このzrevrangeを使うと、PV数のランキングデータを取得するのも2行で済みます。
# PV数1位から20位までの記事を取得
ids = REDIS.zrevrange "posts/dayly/#{Date.today.to_s}", 0, 19
@posts = Post.where(id: ids)