使用Rails + Redis + AWS来存储PV数量
我将介绍一种在AWS上运行的Rails应用程序中保存页面浏览量的方法。
本文只涵盖基本内容,但可以应用于例如博客的每日PV排名、个别页面PV数量的趋势图等情况。
将Redis添加到AWS中
在AWS上有一个专门针对Redis和Memcached等NoSQL的服务叫做ElastiCache。
由于创建新的Redis实例的步骤很简单,请参考其他网站的文章来添加Redis。
我使用AWS OpsWorks将AWS ElastiCache for Redis作为参考网站。
请在AWS上添加Redis,并转到实例的详细页面。

在使用Rails和Redis时,我们可以使用屏幕的端口和端点,所以需要将其添加到Rails的环境变量中。
ENV["REDIS"] = "xxxx.xxxx.chache.amazonaws.com:6379" # Endpoint + Port
如果您希望在本地使用Redis而不是AWS,并且由于在AWS上添加的Redis只能从AWS的Rails实例连接,所以无法从本地连接到ElastiCache上的Redis,请按照以下步骤安装和配置Redis。
1. 在Mac上安装Redis。
$ brew install redis
$ redis-server
2. 添加Rails环境变量
ENV["REDIS"] = "localhost:6379"
使用Rails配置Redis的设置
为了处理Redis,需要添加一个Ruby gem到Rails中。
gem 'redis'
$ bundle install
将Redis的初始设定添加到initializer文件夹中。
require 'redis'
uri = URI.parse(ENV["REDIS"])
REDIS = Redis.new(host: uri.host, port: uri.port)
通过从Rails调用这个变量,现在可以使用Redis了。
将PV数保存到Redis
假设我们考虑在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)」。
就像这样,每个页面浏览数(PV)根据ID被保存在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数
如果我们能获取到所有文章的页面访问量,就能够每日制作出热门文章排行榜。
我去超市购物了。
@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提供了一种称为有序集合的类型。使用这个类型可以自动输出排名,非常方便。
ソート済みセットの関数「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可以在2行内获取PV数的排名数据。
# PV数1位から20位までの記事を取得
ids = REDIS.zrevrange "posts/dayly/#{Date.today.to_s}", 0, 19
@posts = Post.where(id: ids)