数据收集与可视化
收集生产数据的必要性
因开发环境和测试环境获取用户体验指标数据,用于评估优化效果存在两大明显弊端:
- 样本总量少:开发、测试环境中用户数量有限,收集到的数据量太少,数据的波动会很大;
- 不惧代表性:开发、测试环境缺少真实用户的网络状况、软硬件性能等差异化较大的因素。
所以在生产环境收集用户的真实数据,了解现状、确定优化目标、评估优化效果才是真实可靠的。
推荐数据收集工具
- Prometheus 是一款开源的数据监控解决方案,主要包括以下模块:
- 面相各种编程语言的数据采集 SDK(prom-client for Node.js)
- 接收数据上报的服务端应用
- 基于时序数据库
- 基础数据可视化前端应用
- Grafana 是一款开源的数据可视化工具,主要有以下特性:
- 兼容 Prometheus 在内的各种数据库的数据查询
- 内置海量的可视化图表模板
- 支持免费的私有化部署
Grafana 数据可视化平台
- 基于 Grafana 云端应用
- 本地自建 Node.js 服务器应用,安装 Prometheus 数据收集 SDK(
prom-client
)
1. 创建 Grafana Cloud 账户
访问 https://grafana.com/get/ 创建免费的 Grafana Cloud 账户,永久免费的权益包括:
- 10k metrics
- 50GB logs
- 50GB traces
- 50GB profiles
- 500VUh k6 testing
- 50k frontend sessions
- 3 active users
- 14 day retention
使用免费账户登录后进度 HOME 页面:
2. 创建基于 Node.js 仪表盘
点击首页的 Get started 按钮开始设置 Grafana Cloud:
创建基于 Node.js 端的数据连接到 Grafana Cloud 平台:
3. 关于集成的配置细节
根据实际环境选择平台,后续操作步骤会根据不同的平台有所变化:
通过输入 Token name 创建新的 Token 或使用已有 Token 激活 Grafana Alloy 安装步骤:
- 按提示命令安装并运行 Grafana Alloy;
- 按提示命令对 Grafana Alloy 进行配置;
- 按提示命令运行 Grafana Alloy;
- 通过 Test Alloy connection 菜单验证连接状态;
根据实际环境选择 Simple set-up 或 Advanced set-up,仅单机演示选择 Simple set-up 即可:
准备配置文件:
搭建 Node.js 运行环境:在 Node.js 服务中安装
prom-client
模块后启用默认指标,并将指标公开在/metrics
端点;javascriptimport express from 'express'; import { collectDefaultMetrics, register } from 'prom-client'; collectDefaultMetrics(); const app = express(); app.get('/metrics', async (_req, res) => { try { res.set('Content-Type', register.contentType); res.end(await register.metrics()); } catch (err) { res.status(500).end(err); } }); app.listen(4001, '0.0.0.0');
设置 Grafana Alloy :将下面的配置追加到
/etc/alloy/config.alloy
配置文件;discovery.relabel "metrics_integrations_integrations_nodejs" { targets = [{ __address__ = "localhost:4001", }] rule { target_label = "instance" replacement = constants.hostname } } prometheus.scrape "metrics_integrations_integrations_nodejs" { targets = discovery.relabel.metrics_integrations_integrations_nodejs.output forward_to = [prometheus.relabel.metrics_integrations_integrations_nodejs.receiver] job_name = "integrations/nodejs" } prometheus.relabel "metrics_integrations_integrations_nodejs" { forward_to = [prometheus.remote_write.metrics_service.receiver] rule { source_labels = ["__name__"] regex = "up|nodejs_active_handles_total|nodejs_active_requests_total|nodejs_eventloop_lag_p50_seconds|nodejs_eventloop_lag_p99_seconds|nodejs_eventloop_lag_seconds|nodejs_external_memory_bytes|nodejs_gc_duration_seconds_count|nodejs_gc_duration_seconds_sum|nodejs_heap_size_total_bytes|nodejs_heap_size_used_bytes|nodejs_heap_space_size_used_bytes|nodejs_version_info|process_cpu_seconds_total|process_cpu_system_seconds_total|process_cpu_user_seconds_total|process_resident_memory_bytes|process_start_time_seconds" action = "keep" } }
运行命令重启 Grafana Alloy,重启后点击 Test connection 按钮测试连接状态:
./alloy-darwin-amd64 run /etc/alloy/config.alloy
安装仪表盘和警告窗口:点击 Install 按钮自动安装;
查看仪表盘和警告窗口:默认数据将每隔 30s 刷新一次;
4. 上报自定义指标
添加接收 POST 请求的 /metrics
端点,用来接收前端页面通过 web-vitals 收集到的指标数据,按 Prometheus 中自定义指标规范发送到 prom-client。
// 接收 JSON 数据时需要添加此中间件
app.use(express.json());
// 用于接收前端页面的指标数据
app.post('/metrics', async (req, res) => {
try {
const { name, help, labels } = req.body;
if (!name) {
res.status(400).end('name is required');
}
if (!help) {
res.status(400).end('help is required');
}
res.status(200).end();
} catch (err) {
res.status(500).end(err);
}
});
使用 Counter 计数器类型作为自定义指标的收集方案,具体可访问 prom-client 详细了解自定义指标的方式:
// prom-client.js
import client from 'prom-client';
const register = new client.Registry();
client.collectDefaultMetrics({ register });
function useCounter({ name, help, labels = [] }) {
let counter = register.getSingleMetric(name);
if (!counter) {
counter = new client.Counter({
name,
help,
registers: [register],
labelNames: Object.keys(labels),
});
}
counter.inc(labels, 1);
}
export { register, useCounter };
将 useCounter 添加到 POST /metrics
端点下:
import express from 'express';
import { useCounter, register } from './prom-client.js';
const app = express();
app.use(express.json());
app.get('/metrics', async (_req, res) => {
// ...
});
app.post('/metrics', async (req, res) => {
try {
const { name, help, labels } = req.body;
if (!name) {
res.status(400).end('name is required');
}
if (!help) {
res.status(400).end('help is required');
}
// 将数据发送到 prom-client
useCounter({ name, help, labels });
res.status(200).end();
} catch (err) {
res.status(500).end(err);
}
});
app.listen(4001, '0.0.0.0');
在此需要更新 Grafana Alloy 配置文件以增加新指标(FCP)的收集:
prometheus.relabel "metrics_integrations_integrations_nodejs" {
forward_to = [prometheus.remote_write.metrics_service.receiver]
rule {
source_labels = ["__name__"]
regex = "up|FCP|nodejs_active_handles_total|nodejs_active_requests_total|nodejs_eventloop_lag_p50_seconds|nodejs_eventloop_lag_p99_seconds|nodejs_eventloop_lag_seconds|nodejs_external_memory_bytes|nodejs_gc_duration_seconds_count|nodejs_gc_duration_seconds_sum|nodejs_heap_size_total_bytes|nodejs_heap_size_used_bytes|nodejs_heap_space_size_used_bytes|nodejs_version_info|process_cpu_seconds_total|process_cpu_system_seconds_total|process_cpu_user_seconds_total|process_resident_memory_bytes|process_start_time_seconds"
action = "keep"
}
}
PS:更新配置后需要重启 Grafana Alloy;
5. 创建可视化图表
按如图所示菜单创建一个新的仪表盘:
选中默认的数据源:
在输入查询器面板按如图方式添加对 FCP 指标的过滤:
在使用 PostMan 模拟前端上报 FCP 指标数据后,点击刷新图表即可看到图片正常开始渲染数据:
最后分别保存图标和仪表盘: