我们有兴趣记录和计算项目在搜索或列表页面上出现的次数。每天有50k唯一的访客,我们希望我们每天可以产生3-400万个“印象”,这不是一个非常高的数量,但是我们希望建筑师很好。

我们不需要实时读取这些数据,而是希望能够生成日常总数并分析趋势等。类似于业务分析工具。

我们计划在渲染页面后使用AJAX帖子来执行此操作 - 即使这些结果被缓存,这也可以计算结果。我们可以在每个页面的单个帖子中执行此操作,以在页面上发送逗号界的ID列表及其位置。

我希望有某种设计模式/GEM/博客文章可以帮助我避免可能出现的常见初学者错误。我也没有太多经验记录或阅读日志。

我当前的策略 - 使某些内容将事件写入日志文件,以及在一天结束时对结果进行调整并将结果放回MySQL中的后台作业。

有帮助吗?

解决方案

根据列出项目所需的操作,您可能可以在控制器中进行操作并为自己节省往返。您可以使用after_filter进行操作,以使添加不引人注目。

这仅在列出要登录的所有操作的所有操作都需要参数时起作用。这是因为页面缓存忽略了带有参数的请求。

假设您只想在搜索操作上记录搜索数据。

class ItemsController < ApplicationController
  after_filter :log_searches, :only => :search

  def log_searches
    @items.each do |item|
      # write to log here
    end
  end

  ...
  # rest of controller remains unchanged
  ...
end

否则,您可以与Ajax和Onload Remote功能保持正轨。

至于处理处理,您可以使用由Cron作业运行的耙子任务来收集统计信息,并可能更新项目以获得受欢迎程度的评分。

无论哪种方式,您都需要阅读 红宝石记录课. 。了解Cron Jobs和Rake任务也不会受到伤害。

其他提示

好的,我有三种方法:

1)队列

在您的Ajax处理程序中,编写最简单的方法(使用机架中间件或轨道金属)将查询参数推到队列。然后,轮询队列并收集消息。

队列从机架中间件推动的快速速度非常快。我们在非常高的流量网站上使用它来记录相似的数据。

下面是一个示例机架中间件(从我们的应用中提取,可以在<2ms左右的情况下处理请求:

class TrackingMiddleware
  CACHE_BUSTER = {"Cache-Control" => "no-cache, no-store, max-age=0, must-revalidate", "Pragma" => "no-cache", "Expires" => "Fri, 29 Aug 1997 02:14:00 EST"}

  IMAGE_RESPONSE_HEADERS = CACHE_BUSTER.merge("Content-Type" => "image/gif").freeze
  IMAGE_RESPONSE_BODY = [File.open(Rails.root + "public/images/tracker.gif").read].freeze

  def initialize(app)
    @app = app
  end

  def call(env)
    if env["PATH_INFO"] =~ %r{^/track.gif}
      request = Rack::Request.new(env)
      YOUR_QUEUE.push([Time.now, request.GET.symbolize_keys])
      [200, IMAGE_RESPONSE_BODY, IMAGE_RESPONSE_HEADERS]
    else
      @app.call(env)
    end
  end
end

对于队列,我建议 八哥, ,除了美好时光,我什么都没有。

在解析端,我会使用 超级电池工具包, ,但我会这么说,我写了。

2)日志

将所有参数作为查询参数传递到静态文件(/1x1.gif?foo=1&bar=2&baz=3)。这不会撞到轨道堆栈,并且会很快。

当您需要数据时,只需解析日志文件!

这是最好的扩展家庭啤酒方法。

3)Google Analytics(分析)

当Google为您完成时,为什么要处理负载?您会惊讶于Google Analytics(Analytic)的出色表现,在您回家酿造任何东西之前,请检查一下!

这将无限地扩展,因为Google购买服务器的速度比您快。


我可以很久以来对此大声疾呼,但我现在必须走。希望这可以帮助!

这就是我最终所做的 - 现在就足够了,并且有了一些简单的基准测试,我对此感到满意。在将结果暴露给客户之前,我们将观看其在生产中的表现。

组件:

class EventsController < ApplicationController
  def create
    logger = Logger.new("#{RAILS_ROOT}/log/impressions/#{Date.today}.log")
    logger.info "#{DateTime.now.strftime} #{params[:ids]}" unless params[:ids].blank?
    render :nothing => true
  end
end

这是从网站布局中的Ajax调用中调用的...

<% javascript_tag do %>
  var list = '';
  $$('div.item').each(function(item) { list += item.id + ','; });
  <%= remote_function(:url => { :controller => :events, :action => :create}, :with => "'ids=' + list" ) %>
<% end %>

然后,我做了一项耙子任务,将这些逗号分隔的ID行导入到DB中。这是第二天运行:

desc "Calculate impressions"
task :count_impressions => :environment do
  date = ENV['DATE'] || (Date.today - 1).to_s # defaults to yesterday (yyyy-mm-dd)
  file = File.new("log/impressions/#{date}.log", "r")
  item_impressions = {}
  while (line = file.gets)
    ids_string = line.split(' ')[1]
    next unless ids_string
    ids = ids_string.split(',')
    ids.each {|i| item_impressions[i] ||= 0; item_impressions[i] += 1 }
  end
  item_impressions.keys.each do |id|
    ActiveRecord::Base.connection.execute "insert into item_stats(item_id, impression_count, collected_on) values('#{id}',#{item_impressions[id]},'#{date}')", 'Insert Item Stats'
  end

  file.close
end

要注意的一件事 - 在控制器操作中声明了记录器变量 - 不在环境中。RB,就像通常使用记录器一样。我对此进行了基准测试-10000写作时间约为20秒。平均约2毫秒的写作。使用Envirnment.rb中的文件名,大约需要14秒。我们进行了权衡,因此我们可以动态地确定文件名 - 在午夜切换文件的一种简便方法。

目前,我们的主要关注点 - 我们不知道每天将计算多少个不同的项目 - 即。我们不知道尾巴有多长时间。这将确定每天添加多少行。我们希望我们需要限制我们保留日常报告的距离,并在此时进一步提高结果。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top