首页ruby
海外散仙厉飞雨 · 真仙

Rails 中的 ActiveSupport::Notifications 解析

hfpp2012发布于

1. 介绍

ActiveSupport::Notifications是rails内核的一部分,它在rails的整个运行过程中提供了发布订阅的模式,查看rails的源码,会发现,很多组件,比如cache、activerecord、activejob都有它的部分代码实现。说到ActiveSupport::Notifications,先来说说发布订阅模式。这个模式一定有两个端,一个是生产者,一个是消费者,生产者负责生产内容,比如创建了一条记录,再把它的内容放到一个通道中,这个过程就发布,消费者就是通过通道和生产者联系的,消费者订阅这个通道,负责消费或取出内容,它是和生产者对应的,比如上面的生产者创建了一条记录,那消费者取出这条记录的内容,再进行另外的处理,例如,把刚才的记录的数据进行改造再存到数据库中。

它可以轻易实现一个日志系统,或者其他意想不到的功能,这得结合业务。假如要统计一个action被访问的情况,你可能会在这个action做一些before_action或after_action的动作,其实没有必要,rails已经默认为每个action做好了这些动作的,就是已经做好了发布动作,只要在适当的地方,做好订阅那个动作,取出数据就可以了。

2. 使用

rails guides Active Support Instrumentation有列出rails在哪些模块实现了Notifications,比如controller、view、activerecord等,有一些没列出来的,在源码可以搜索得到。

现在我们以controller的Instrumentation实现做一个例子:我们要统计一篇文章的访问量。

先在config/initializers中新增一个文件,叫notification.rb

# config/initializers/notification.rb
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, payload|
  Rails.logger.info payload
end

重启rails应用,之后会在日志中看到类似下面的信息。

{:controller=>"ArticlesController", :action=>"show", :params=>{"controller"=>"articles", "action"=>"show", "id"=>"redis-xian-zi-dong-shu-ru-wan-cheng-ba"}, :format=>:html, :method=>"GET", :path=>"/articles/redis-xian-zi-dong-shu-ru-wan-cheng-ba", :status=>200, :view_runtime=>250.98650199892668, :db_runtime=>791.2345280000001}

现在每次访问一个action,都会有这个信息,都带有controller和action的名称和请求参数等信息,这样我们只要得到这个信息,就可以处理了。比如通过id这个参数,在数据中找到对应的记录,更新相应的字段信息就可以了。

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, payload|
  Rails.logger.info payload
  if payload[:controller] == "ArticlesController" && payload[:action] == "show"
    UpdateArticleVisitCountWorker.perform_async(payload[:params]["id"]) if payload[:params]["id"].present?
  end
end

3. 源码解析

rails的很多模块或组件都有实现Instrumentation,但是我们自己添加的模块也要实现的时候,该怎么办呢?

这点在rails notifications的api中就可以查到如何做,我们先来看看在rails源码中的各组件是如何实现Instrumentation的。

比如关于controller部分的Instrumentation实现。

# https://github.com/rails/rails/blob/7f18ea14c893cb5c9f04d4fda9661126758332b5/actionpack/lib/action_controller/metal/instrumentation.rb#L17
def process_action(*args)
  raw_payload = {
    :controller => self.class.name,
    :action     => self.action_name,
    :params     => request.filtered_parameters,
    :format     => request.format.try(:ref),
    :method     => request.request_method,
    :path       => (request.fullpath rescue "unknown")
  }

  ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)

  ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
    begin
      result = super
      payload[:status] = response.status
      result
    ensure
      append_info_to_payload(payload)
    end
  end
end

具体就是靠ActiveSupport::Notifications.instrument这个指令来实现的,第一个参数接的是通道的名称,第二个是要传递的参数(raw_payload),例如上面日志看到的各种参数信息。

但是这些默认的参数有时候不够用,我们可以在controller上加一些自己的参数。

class Admin::UsersController < Admin::ApplicationController
  def append_info_to_payload(payload)
    super
    payload[:recharged] = @recharged
  end
end

完结。

本站帖子均为原创内容,如需转载请注明出处,谢谢。

0 条回复
暂无回复~~

© 汕尾市求知科技有限公司 | 粤ICP备19038915号 | 在线学员:32

Top