@ bull- board/ 度量衡
状态: Beta. API和Redis的存储布局在特性结算时仍然可能在小放出时会有所改变. 它可以安全运行(opt-in,它只写它自己命名的空格密钥),但如果依赖于存储格式,则会给精确的版本打针.
长期保留的历史工作指标 牛板。 。 。 .
快照将本地公牛MQ每分钟的公牛MQ分量制成长保留雷迪斯桶并曝光出一个
MetricsHistoryProvider 提供牛板的历史图表 万事皆选入:核心
@bull-board/api 无国籍.
先决条件
您的 BullMQ 员工必须启用本地的度量衡, 窗口足够大, 足以生存任何 记录器故障时间, 例如 :
new Worker(name, processor, {
connection,
metrics: { maxDataPoints: MetricsTime.ONE_WEEK },
});
使用量
import { MetricsRecorder, RedisMetricsHistoryProvider } from '@bull-board/metrics';
// In your always-on worker/app process:
const recorder = new MetricsRecorder({
queues: [new BullMQAdapter(queue)],
connection,
retentionDays: 90,
});
recorder.start();
// Where you build the board:
createBullBoard({
queues,
serverAdapter,
options: { historyProvider: new RedisMetricsHistoryProvider({ connection }) },
});
queues 也接受一个函数,在每个勾选上解决,而不是一次,这是在记录器运行时队列设置变化时想要的.
不把公牛板嵌入你自己的应用程序里? 里面的货船 @bull-board/cli 编辑 ghcr.io/felixmosh/bull-board 图像, 其中 --history 登记提供者,并在同一进程中启动记录器。 见 CLI 指南。 。 。 .
关机时打电话 recorder.stop() 财务报告和审定财务报表 provider.disconnect()。如果录音机/提供机在内部打开,两者都只关闭了 Redis 连接,所以如果你自己通过,就安全了 Redis 举个例子.
connection 可能是 ioredis 选项,或者 Redis 或者说 Cluster 你创造了一个实例. ioredis 是一个对等依赖( v5 或 v6 ): 在您的应用程序中解决单个副本, 如果您重新使用一个已有客户端, 请通过从同一个客户端中创建的 ioredis。来自不同安装的客户端(例如,由BullMQ在内部所创建,被固定在不同的ioredis主端)不被确认为客户端,会被误读为选项.
时间戳和桶是协调世界时.
关键命名空间
录音员写的每一把钥匙都放在下面 bull-board:metrics:。通过 prefix 移动它,这就是两个板 共享一个雷迪斯 不运行历史在一起:
const recorder = new MetricsRecorder({ queues, connection, prefix: 'staging:metrics' });
const provider = new RedisMetricsHistoryProvider({ connection, prefix: 'staging:metrics' });
供应商、记录员和任何 MetricsHistoryAdmin 全部必须获得相同的前缀。 一个读取命名空间的提供者不会写到报告空历史而不是错误,同样,不匹配的保留会悄悄地缩短窗口.
Redis 集群
传出 a Cluster 作为 connection 它的工作,有一件事值得知道 关于关键布局.
每张快照都写着三等队和三等队 __global__ 单层卷起 EVAL,这就使得所有决议的写作同时具有一能性。 Redis Cluster拒绝一个多键指令,其键会降落在不同槽中,因此整个命名空间必须散去一个槽. 它被赋予 散列标签 为此: bull-board:metrics 变成 {bull-board:metrics},则a prefix 除非它已经携带了 {...} 标记,在此情况下,您将使用您的位置并选择位置.
一个插槽意味着一个大师掌握着整个棋盘的历史. 这就是保持卷起一致的写法,而不是重算读取, 音量与下面的存储部分相同: 大约50 MB在200个繁忙的队列上,以及一个 EVAL 每分每分每分每分每队.
独立的密钥没有标记,也没有变化,所以对现有部署没有任何动作. 由于关键名称不同,从一个单独的Redis到一个集群,没有什么能延续下去.
随机抽样通过相同的连接读取了 BullMQ 自己的密钥, 因此您的队列需要挂有散列标记的前缀 BullMQ 在集群模式中已经要求( )new Queue(name, { prefix: '{bull}' })) (中文(简体) ). 没有它,采样器的管道跨出槽; 它吞下错误,所以通过 onLatencyError 来看看这个.
CLI 和 Docker 图像以 --cluster时 --history 一样的工作方式.
长期工作
记录器除了完成/失效的计数器之外,还跟踪每个队列的两个直方图:等待时间(processedOn - timestamp工作在工人拿起之前坐了多久 并运行时间finishedOn - processedOn处理者花了多长时间 他们诊断出不同的问题,所以它们被分开,而不是合并成一个数字.
两者都是通过扫描完成的和失败的分类集来收集的(BallMQ通过完成时间来评分它们) moveToFinished是因为 ZADD)通过记录器已存在的勾子上的水印,所以不需要工人改变,也没有先决条件 queue.getMetrics()。 。 。 .
等待直方图只看到已完成的工作, 所以当队列被备份而工作停止完成时, 它就会安静下来 。 排队年龄测量仪(最老的工作仍在等待)也因此被记录下来,UI在等待图上将其叠加. 复习时间仅不包括等待时间,因为 timestamp 是一个工作创造,但 processedOn 这是它的最新尝试。 百分位是以桶宽为界的估计;桶平面布局是固定的,不是可配置的,因为两个布局不同的范围不能被合并为一百分位.
removeOnComplete: true 任务完成后立即删除, 因此没有剩余扫描; 该队列永远不会显示延迟数据 。 手动删除、清理或重试的工作,对上个滴答以来完成的工作都一样。 对应图表不受影响,因为BullMQ在它完成时会算一个工作,被移除时永远不会减少. 默认情况下进行随机抽样; 设定 latency: false 打开 MetricsRecorder 把它关掉.
PostgreSQL 备份队列
由 PostgreSQL 支持的 BullMQ 6 队列没有记录历史 。 内容 getMetrics() 报告 prevTS 作为0,它留下了每分钟缓冲可达表,因此计数器被丢弃而不是从记录器的时钟上取出;字段被跟踪到后端的计数器中,这样就可能解决上游问题. 紧急采样需要BullMQ的 Redis 密钥, 而这样的队列没有这些密钥, 因此它被跳过而不是记录为空的积压 。 在同一块板上重排的队列不受影响.
存储
每张快照一并写出三个决议,每个决议都有自己的保留,因为它们的金额大相径庭:
| 级别 | 默认保留 | 每个繁忙日、每个队列和公尺大小 |
|---|---|---|
| 分钟 | 7天时间 | ~72千贝 |
| 小时 | 90天时间 | ~0.3 KB 键 |
| 天数 | 90天时间 | ~15 字节 |
默认每分每分每分 每分每分每分每分 每分每分每分每分每分每分 每分每分每分每分每分每分每分 每分每分每分每分每分每分每分每分每分每分 没有活动的分钟从不写,所以脚印跟随排队的繁忙程度,而不是记录的时间.
const recorder = new MetricsRecorder({
queues,
connection,
retention: { minutes: 7, hours: 90, days: 90 },
});
分钟窗口是值得调用的一个窗口:它基本上持有所有的字节,在停机后作为录音员的接地窗口而翻了一番. retentionDays: N 仍然工作,并设置小时和每日窗口,在默认时离开分钟窗口.
短距离直方图和队列年龄表使用单独的打包格式,在保留90天时单独测量:
| 假设 | 计量 |
|---|---|
| 1个队列, 双直方图和队列年龄测量, 现实的分布 | 254.5 KB (韩语) |
| 同样的,病理:所有18个水桶每小时都有大量人口 | 574.6 KB (韩语) |
共享 __global__ 跨队列滚动 | ~224 KB 一次,整个板 |
卷起是一个单一的分担成本,而不是每个队列相乘:200个队列在典型流量上大约是200×254.5KB,大约是50MB,加上一个共享的224KB卷起. sample() 完成1 000个任务时,每勾取约9.11 ms;每勾取约12个Redis轮回行程,每勾取12个队列,平分工作计数;上面有一个子抽样盖 maxLatencySamplesPerTick 连一万份工作也一样.
保留由Redis强制执行。 日视键会自用TTL过期;每日总散列会随着每一新日的卷入被剪接到窗口.
检查和清理历史
import { MetricsHistoryAdmin } from '@bull-board/metrics';
const admin = new MetricsHistoryAdmin({ connection }); // add `prefix` if the recorder has one
await admin.stats(); // bytes per tier and per queue, day range
await admin.purge(); // delete everything
await admin.purge({ queue: 'mailer' }); // delete one queue
await admin.purge({ before: '2026-06-01' }); // delete anything older than a day
两者都是 SCAN- 驱动和限制在这个软件包的命名空间, 所以他们从来没有屏蔽Redis, 也从来没有碰BullMQ自己的钥匙。 在一个集群上,他们扫描每个主人,因为 SCAN 没有客户端的路由键 。 清理一队队也从交叉队列上取出. 打电话 admin.disconnect() 当完成.
RedisMetricsHistoryProvider 把同样的两个操作暴露在板上,让它们变成Metrics历史页面上的存储面板,并在删除任何内容之前得到确认.
范围
运出公牛板的UI每天读取卷起. getHistory 也支持定制消费者的小时颗粒度(通过核心 /api/metrics/history 端点),虽然内置图表没有使用.