使用 InfluxDB 监控 Ruby on Rails

导航至

本文由 Aniket Bhattacharyea 撰写,最初发表于 The New Stack。向下滚动查看作者简介和照片。

像 InfluxDB 这样的时序数据库是专门用于处理 时序数据的数据库,时序数据是按时间索引的数据。与传统数据库不同, 时序数据库 针对数据的读取和写入进行了优化,而对更新或删除数据的性能考虑较少。

由于时序数据的时间依赖性,时序数据库非常适合应用程序监控。如果您有 Ruby on Rails 应用程序,监控应用程序的性能以确保其平稳运行至关重要。

使用时序数据库,您可以存储来自应用程序的指标,例如已处理的请求数、消耗的内存量以及生成响应所花费的时间。在本文中,您将学习如何使用 InfluxDB 监控 Ruby on Rails 应用程序。

如果您想克隆项目并在自己的编辑器中跟随操作,这是 GitHub 仓库的链接。

先决条件

要学习本教程,您需要以下内容

  1. 您的计算机上已安装并设置 Docker。您需要 Docker 来运行 InfluxDB 服务器。或者,如果您不想在计算机上安装任何东西,可以使用 InfluxDB Cloud 获取免费运行的 InfluxDB 实例。
  2. 您的计算机上已安装最新版本的 Ruby on Rails。您可以运行 rails -v 来检查 Rails 是否正确安装。

设置 InfluxDB

在本文中,您将使用 Docker 运行本地 InfluxDB 实例。首先,使用以下命令启动 InfluxDB 容器

docker run --name influxdb -d -p 8086:8086 influxdb:2.1.1

在浏览器中访问 http://localhost:8086 以访问 InfluxDB 欢迎界面。您需要提供用户名和密码,并创建一个新的组织和存储桶。

Creating-a-new-user

单击“稍后配置”以访问仪表板。

在左侧边栏中,您将看到一个名为“数据”的菜单项。单击它并选择“API 令牌”选项卡。在那里,您会找到 InfluxDB 创建的默认令牌。将此令牌保存在安全的地方,以便您稍后在本教程中再次访问它。

The-API-Tokens-tab

设置 Rails 应用程序

接下来,您需要使用 rails new 命令创建一个新的 Rails 应用程序

rails new influxdb-demo

然后移动到 influxdb-demo 目录

cd influxdb-demo

请注意,此应用程序使用 SQLite 数据库,因为这仅用于演示目的,但您也可以使用 PostgreSQL 或 MySQL

接下来,您需要使用 rails db:create 命令创建数据库

rails db:create

由于您将在应用程序中检测数据库交互,因此您需要一些与数据库交互的东西。您可以使用 Rails 的 scaffold 功能快速生成功能齐全的 CRUD API,而无需编写任何代码

rails g scaffold post title:text body:text

这将生成一个 Post 模型,其中包含 titlebody 属性以及必要的 CRUD 端点。

通过运行 rails db:migrate 迁移数据库。

InfluxDB 有一个 influxdb-rails gem,可以自动检测 Rails 应用程序,但在撰写本文时,它不支持 InfluxDB 2.0,因此您将改用 influxdb-client gem。打开 Gemfile 并添加以下行

gem "influxdb-client", "~> 2.3.0"

然后通过运行 bundle install 安装 gem。

设置检测

您可能想知道,“我究竟该如何从 Rails 应用程序收集指标?” 幸运的是,像 Rails 中的其他一切一样,检测也是内置的。Active Support Instrumentation API 为您提供了无数的 Action Controller、Active Storage、Action Mailer、Active Record、Action View 和其他您可以订阅的事件。它还使您能够检测自定义事件。

要订阅事件,您需要使用 ActiveSupport::Notifications.subscribe 和一个在事件触发时调用的代码块。该代码块接收事件的名称、开始和结束时间、触发事件的检测器的唯一 ID 以及包含取决于事件的不同信息的有效负载。例如,以下代码订阅 process_action.action_controller 事件,该事件将在 Rails 应用程序处理请求时触发

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
  Rails.logger.info "#{name} Received! (started: #{started}, finished: #{finished})" 
end

在本文中,您将订阅以下事件,但您可以根据需要添加任意数量的事件

  1. process_action.action_controller:在处理请求时触发
  2. render_template.action_view:在渲染模板时触发
  3. sql.active_record:在执行 SQL 查询时触发
  4. instantiation.active_record:在一个或多个 Active Record 对象被实例化时触发

创建一个新的 config/initializers/influxdb.rb 文件,您最终将在其中添加逻辑。

现在,您需要通过实现以下代码来实例化 InfluxDB 客户端

client = InfluxDB2::Client.new(
    'http://localhost:8086',
    'YOUR_TOKEN',
    bucket: 'YOUR_BUCKET',
    org: 'YOUR_ORGANIZATION',
    precision: InfluxDB2::WritePrecision::NANOSECOND,
    use_ssl: false
)
write_api = client.create_write_api

YOUR_TOKEN 替换为您从 InfluxDB 获取的令牌,将 YOUR_BUCKET 替换为存储桶名称,并将 YOUR_ORGANIZATION 替换为组织名称。如果您使用的是云帐户,您还需要将 localhost URL 替换为您的云区域 URL。

现在,我们需要订阅第一个事件。以下代码设置订阅

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
    # Send data to influxdb
end

在代码块内,您需要设置要发送到 InfluxDB 的数据点。您可以使用 四种数据格式。在这里,您将使用哈希

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
    hash = {
        name: "process_action.action_controller",
        tags: { 
            method: "#{data[:controller]}##{data[:action]}",
            format: data[:format],
            http_method: data[:method],
            status: data[:status],
            exception: data[:exception]&.first
         },
         fields: {
            time_in_controller: (finished - started) * 1000,
            time_in_view: (data[:view_runtime] || 0).ceil,
            time_in_db: (data[:db_runtime] || 0).ceil,
        },
        time: started
    }

    write_api.write(data: hash)
end

在上面的代码中,name 键表示测量的名称,该名称设置为事件的名称。tags 键设置 标签

在代码中,使用了五个标签

  1. 调用的 Ruby 方法。这将是处理请求的控制器类的方法。
  2. 请求的格式 (html/json/xml)。
  3. 请求的 HTTP 方法 (GET, POST, PUT 等)。
  4. 响应的 HTTP 状态代码。
  5. 发生的异常 (如果有)。

fields 键保存测量的 字段,并使用以下三个字段

  1. 处理请求所花费的总时间 (以毫秒为单位)。
  2. 渲染视图所花费的总时间 (以毫秒为单位)。
  3. 查询数据库所花费的总时间 (以毫秒为单位)。

最后,time 键用于设置数据的时间戳。此键的值是事件触发的时间。

数据点设置完成后,write_api.write 用于将数据写入 InfluxDB。

第二个订阅是针对 render_template.action_view 事件

ActiveSupport::Notifications.subscribe "render_template.action_view" do |name, started, finished, unique_id, data|
    hash = {
        name: "render_template.action_view",
        tags: { 
            identifier: data[:identifier],
            layout: data[:layout],
            exception: data[:exception]&.first
         },
         fields: {
            duration: (finished - started) * 1000
        },
        time: started
    }

    write_api.write(data: hash)
end

这次唯一的区别在于 tagsfields 键。identifier 标签告诉渲染了哪个模板,layout 标签告诉应用了哪个布局。duration 字段表示渲染模板所花费的时间 (以毫秒为单位)。

第三个订阅是针对 sql.active_record 事件

ActiveSupport::Notifications.subscribe "sql.active_record" do |name, started, finished, unique_id, data|
    hash = {
        name: "sql.active_record",
        tags: { 
            name: data[:name],
            statement_name: data[:statement_name],
            exception: data[:exception]&.first
         },
         fields: {
            duration: (finished - started) * 1000
        },
        time: started
    }

    write_api.write(data: hash)
end

在这里,name 标签保存操作的名称,statement_name 标签保存 SQL 语句的名称。duration 字段计算查询执行所花费的时间 (以毫秒为单位)。

最后一个订阅是针对 instantiation.active_record 字段

ActiveSupport::Notifications.subscribe "instantiation.active_record" do |name, started, finished, unique_id, data|
    hash = {
        name: "instantiation.active_record",
        tags: { 
            class_name: data[:class_name],
            exception: data[:exception]&.first
         },
         fields: {
            duration: (finished - started) * 1000,
            record_count: data[:record_count]
        },
        time: started
    }

    write_api.write(data: hash)
end

class_name 标签表示实例化的 Active Record 对象的类名。duration 字段表示实例化对象所花费的时间 (以毫秒为单位),record_count 字段表示实例化了多少条记录。

设置就完成了!现在您可以开始使用应用程序生成指标。

生成指标

检测设置完成后,就可以使用应用程序生成一些指标了。通过运行 rails s 启动 Rails 服务器,并访问 http://localhost:3000/posts。您将看到帖子索引页面

Posts-index-page

单击“新帖子”以创建新帖子。

New-post-page

输入标题和正文,然后单击“创建帖子”。

Post-created-successfully

单击“返回帖子”以返回帖子列表。

The-posts-list

单击任何帖子下的“显示此帖子”以在单独的页面上查看帖子。

Post-details-page

单击“编辑此帖子”进行编辑,或单击“删除此帖子”进行删除。

现在您可以根据需要创建、编辑和删除任意数量的帖子。完成后,返回 InfluxDB 仪表板以可视化数据。

可视化数据

在 InfluxDB 仪表板中,单击“Explore”选项卡。在这里,您将看到一个交互式查询构建器,可让您可视化您选择的数据。让我们首先可视化创建帖子花费了多少时间。

首先,在“From”选项卡中选择您的存储桶

Selecting the bucket

后续选项卡将让您选择如何过滤数据。

确保在下一个选项卡中选择 _measurement,并从列表中选择 process_action.action_controller

Selecting the measurement

在以下过滤器面板中选择 method 作为键,并选择 PostController#create 作为标签

Selecting the method

最后,在 _field 过滤器中选择三个字段

Selecting the fields

从右侧的下拉列表中选择适当的时间范围 (例如,“过去 15 分钟”),然后单击“提交”。您将看到一个带有数据的闪亮新图表

Visualizing-the-data

现在,让我们可视化渲染视图所花费的时间。这次,您将使用 Flux 查询数据。

单击“脚本编辑器”,这将打开一个编辑器窗口,您可以在其中编写 Flux 查询。然后粘贴以下查询

from(bucket: "influxdb-rails")
  |> range(start: -20m)
  |> filter(fn: (r) => r["_measurement"] == "render_template.action_view")
  |> filter(fn: (r) => r["_field"] == "duration")
  |> aggregateWindow(every: 30s, fn: mean)

单击“提交”,您将看到数据以图表形式显示。

Graph-using-Flux

继续尝试您收集的所有不同指标。

结论

InfluxDB 使收集和可视化来自 Ruby on Rails 应用程序的检测数据变得容易。使用 ActiveSupport instrumentation API,您可以从应用程序收集不同的指标并将它们存储在 InfluxDB 中。

本文仅向您展示了您可以收集的少量指标。您可以在此处找到指标的完整列表。如果没有内置指标适合您的需求,您也可以创建自己的。一切皆有可能!

关于作者

Aniket 是一名数学硕士研究生,对计算机和软件充满热情。他喜欢探索与编码相关的各个领域,并使用 Ruby on Rails 和 Vue.JS 作为 Web 开发人员工作。

Profile photo of Aniket Bhattacharyea