使用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,并转到实例的详细页面。

elasticache-instance-details

在使用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)
bannerAds