世界上最伟大的投资就是投资自己的教育

首页Ruby
随风 · 练气

Rails 中的 ActiveSupport::Notifications 解析

随风发布于3814 次阅读

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 条回复
暂无回复~~
喜欢
统计信息
    学员: 29980
    视频数量: 1996
    文章数量: 526

© 汕尾市求知科技有限公司 | Rails365 Gitlab | 知乎 | b 站 | csdn

粤公网安备 44152102000088号粤公网安备 44152102000088号 | 粤ICP备19038915号

Top