iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0

Metrics

Moleculer 內建的 Metrics 可以收集系統內部大量的流程指標,而且可以很簡單的定義你自己的客製化 Metrics。系統內建了幾個 Metrics 報表產生器,如: ConsolePrometheusDatadog 等。

注意, Moleculer 作者 icebob 在此討論串提到[2] ,近期的趨勢 OpenTelemetry[3] 即將成為業界標準,而它與目前內建的 Metrics 與 Tracing 模組功能幾乎相同,作者可能會在下一個版本 v0.15 切換到 OpenTelemetry ,因此你可能需要考慮是否要使用目前內建的模組。
更新,在同一個討論串作者說明 OpenTelemetry 不會在 v0.15 實施,但是會以 middleware 方式支援,範例在 next branch [7] 。

以下介紹的各種報表產生器,其中細部設定筆者未測試或確認過的部分,本文將暫時保留官方提供的範例註解說明。

假如你想使用舊版的 Metrics (小於 v0.14) ,請使用 EventLegacy 的追蹤輸出器[4] 。

範例:啟用 Metrics 功能,並設定報表產生器

名稱 類型 預設值 說明
enabled <Boolean> false 啟用 Metrics 功能.
reporter <Object> | <Object[]> null Metric 報表產生器設置,詳情文章後面會說明。
collectProcessMetrics <Boolean> process.env.NODE_ENV !== "test" 收集流程與系統相關 metrics 。
collectInterval <Number> 5 收集時間區段(秒)
defaultBuckets <Number[]> [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10] 直方圖的桶值
defaultQuantiles <Number[]> [0.5, 0.9, 0.95, 0.99, 0.999] 直方圖分位數
defaultMaxAgeSeconds <Number> 60 分位數的最大壽命(秒)
defaultAgeBuckets <Number> 10 分位數計算的桶數
defaultAggregator <String> sum 數值聚合方法

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            "Console"
        ]
    }
};

Metrics 報表產生器

名稱 類型 預設值 說明
includes <String> | <String[]> null 要輸出的 Metrics 清單
excludes <String> | <String[]> null 不輸出的 Metrics 清單
metricNamePrefix <String> null Metrics 名稱前綴
metricNameSuffix <String> null Metrics 名稱後綴
metricNameFormatter <Function> null Metric 格式化函數
labelNameFormatter <Function> null Metric 標籤名稱格式化函數

範例:選項設定方式

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            {
                type: "Console",
                options: {
                    includes: ["moleculer.**.total"],
                    excludes: ["moleculer.broker.**", "moleculer.request.**"],

                    // 原始 "moleculer.node.type" ,加上前綴: "mol:moleculer.node.type"
                    metricNamePrefix: "mol:",
                    // 原始 "moleculer.node.type" ,加上後綴: "moleculer.node.type.value"
                    metricNameSuffix: ".value",

                    metricNameFormatter: name => name.toUpperCase().replace(/[.:]/g, "_"),
                    labelNameFormatter: name => name.toUpperCase().replace(/[.:]/g, "_")
                }
            }
        ]
    }
};

Console

這是一個除錯用的報表產生器,它會定期將 metrics 輸出至主控台。

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            {
                type: "Console",
                options: {
                    // 輸出頻率(秒)
                    interval: 5,
                    // 客製化 logger
                    logger: null,
                    // 使用顏色
                    colors: true,
                    // 不要輸出完整清單,只輸出變更的 metrics
                    onlyChanges: true
                }
            }
        ]
    }
};

CSV

報表產生器會將變更的內容儲存至 Comma-Separated Values (CSV) 檔案。

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            {
                type: "CSV",
                options: {
                    // CSV 檔案輸出目錄
                    folder: "./reports/metrics",
                    // CSV 欄位分隔符號
                    delimiter: ",",
                    // CSV 段落符號
                    rowDelimiter: "\n",
                    // 儲存模式
                    // [metric] 儲存至個別的檔案
                    // [label] 按標籤儲存至個別的檔案
                    mode: "metric",
                    // 儲存的 metrics 類型
                    types: null,
                    // 儲存間隔時間(秒)
                    interval: 5,
                    // 客製化檔名格式化器
                    filenameFormatter: null,
                    // 客製化 CSV 行格式化器
                    rowFormatter: null,
                }
            }
        ]
    }
};

事件

事件報表產生器會將 metric 值發送至 Moleculer 事件。

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            {
                type: "Event",
                options: {
                    // 事件名稱
                    eventName: "$metrics.snapshot",
                    // 是否為廣播事件
                    broadcast: false,
                    // 事件群組
                    groups: null,
                    // 只輸出變更的 metrics
                    onlyChanges: false,
                    // 輸出頻率(秒)
                    interval: 5,
                }
            }
        ]
    }
};

Datadog

Datadog 報表產生器會將 metric 發送至 Datadog 伺服器。

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            {
                type: "Datadog",
                options: {
                    // Hostname
                    host: "my-host",
                    // Base URL
                    baseUrl: "https://api.datadoghq.com/api/",
                    // API 版本
                    apiVersion: "v1",
                    // 伺服器 URL 路徑
                    path: "/series",
                    // Datadog API Key
                    apiKey: process.env.DATADOG_API_KEY,
                    // 附加到所有 metrics 標籤的預設標籤
                    defaultLabels: (registry) => ({
                        namespace: registry.broker.namespace,
                        nodeID: registry.broker.nodeID
                    }),
                    // 輸出頻率(秒)
                    interval: 10
                }
            }
        ]
    }
};

Prometheus

Prometheus 報表產生器會將 metric 以 Prometheus 的格式暴露輸出,Prometheus 伺服器可以利用它收集資料,預設連接埠為 3030。

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            {
                type: "Prometheus",
                options: {
                    // HTTP 連接埠
                    port: 3030,
                    // HTTP URL 路徑
                    path: "/metrics",
                    // 附加到所有 metrics 標籤的預設標籤
                    defaultLabels: registry => ({
                        namespace: registry.broker.namespace,
                        nodeID: registry.broker.nodeID
                    })
                }
            }
        ]
    }
};

StatsD

StatsD 報表產生器會透過 UDP 將 metric 發送至 StatsD 伺服器。

moleculer.config.js

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            {
                type: "StatsD",
                options: {
                    // 伺服器 host
                    host: "localhost",
                    // 伺服器 port
                    port: 8125,
                    // 最大酬載大小
                    maxPayloadSize: 1300
                }
            }
        ]
    }
};

客製化報表產生器

你也可以建立客製化的報表產生器,官方建議可以參考 Console Reporter[5] 的原始碼來修改,再實作 initstopmetricChanged 方法。

範例:建立客製化報表產生器

my-metrics-reporter.js

const BaseReporter = require("moleculer").MetricReporters.Base;

class MyMetricsReporter extends BaseReporter {
    init() { /*...*/ }
    stop() { /*...*/ }
    metricChanged() { /*...*/ }
}

module.exports = MyMetricsReporter;

範例:使用客製化報表產生器

moleculer.config.js

const MyMetricsReporter = require("./my-metrics-reporter");

module.exports = {
    metrics: {
        enabled: true,
        reporter: [
            new MyMetricsReporter(),
        ]
    }
};

Metric 支援類型

計數器

counter 計數器是對 Metric 單純的累加,它只能遞增或是歸零。例如你可以使用計數器來表示服務的請求數、任務完成數或錯誤數。速率為每分鐘。

計數器提供的方法:

increment(labels?: GenericObject, value?: number, timestamp?: number)
set(value: number, labels?: GenericObject, timestamp?: number)

測量器

gauge 測量是 metric 的一個可任意增減的單純數值。通常用於測量某個值,例如目前的記憶體使用量,但也可以用在會上下浮動的計數,例如併發請求時的數量。速率為每分鐘。

測量器提供的方法:

increment(labels?: GenericObject, value?: number, timestamp?: number)
decrement(labels?: GenericObject, value?: number, timestamp?: number)
set(value: number, labels?: GenericObject, timestamp?: number)

直方圖

histogram 直方圖會採樣觀察結果並且在可配置的桶中做計數(通常是觀察請求的時間或響應的大小)。另外也提供觀察值的總和,並且可以在時間視窗內計算配置分位數。速率為每分鐘。

直方圖提供的方法:

observe(value: number, labels?: GenericObject, timestamp?: number)

資訊

info 提供關於處理程序的參數、主機名稱或版本號的字串資料或數字。

set(value: any | null, labels?: GenericObject, timestamp?: number)

內建的內部 Metrics

處理程序相關 metrics

  • process.arguments (資訊)
  • process.pid (資訊)
  • process.ppid (資訊)
  • process.eventloop.lag.min (測量)
  • process.eventloop.lag.avg (測量)
  • process.eventloop.lag.max (測量)
  • process.eventloop.lag.count (測量)
  • process.memory.heap.size.total (測量)
  • process.memory.heap.size.used (測量)
  • process.memory.rss (測量)
  • process.memory.external (測量)
  • process.memory.heap.space.size.total (測量)
  • process.memory.heap.space.size.used (測量)
  • process.memory.heap.space.size.available (測量)
  • process.memory.heap.space.size.physical (測量)
  • process.memory.heap.stat.heap.size.total (測量)
  • process.memory.heap.stat.executable.size.total (測量)
  • process.memory.heap.stat.physical.size.total (測量)
  • process.memory.heap.stat.available.size.total (測量)
  • process.memory.heap.stat.used.heap.size (測量)
  • process.memory.heap.stat.heap.size.limit (測量)
  • process.memory.heap.stat.mallocated.memory (測量)
  • process.memory.heap.stat.peak.mallocated.memory (測量)
  • process.memory.heap.stat.zap.garbage (測量)
  • process.uptime (測量)
  • process.internal.active.handles (測量)
  • process.internal.active.requests (測量)
  • process.versions.node (資訊)
  • process.gc.time (測量)
  • process.gc.total.time (測量)
  • process.gc.executed.total (測量)

OS 相關 metrics

  • os.memory.free (測量)
  • os.memory.total (測量)
  • os.memory.used (測量)
  • os.uptime (測量)
  • os.type (資訊)
  • os.release (資訊)
  • os.hostname (資訊)
  • os.arch (資訊)
  • os.platform (資訊)
  • os.user.uid (資訊)
  • os.user.gid (資訊)
  • os.user.username (資訊)
  • os.user.homedir (資訊)
  • os.network.address (資訊)
  • os.network.mac (資訊)
  • os.datetime.unix (測量)
  • os.datetime.iso (資訊)
  • os.datetime.utc (資訊)
  • os.datetime.tz.offset (測量)
  • os.cpu.load.1 (測量)
  • os.cpu.load.5 (測量)
  • os.cpu.load.15 (測量)
  • os.cpu.utilization (測量)
  • os.cpu.user (測量)
  • os.cpu.system (測量)
  • os.cpu.total (測量)
  • os.cpu.info.model (資訊)
  • os.cpu.info.speed (測量)
  • os.cpu.info.times.user (測量)
  • os.cpu.info.times.sys (測量)

Moleculer 相關 metrics

  • moleculer.node.type (資訊)
  • moleculer.node.versions.moleculer (資訊)
  • moleculer.node.versions.protocol (資訊)
  • moleculer.broker.namespace (資訊)
  • moleculer.broker.started (測量)
  • moleculer.broker.local.services.total (測量)
  • moleculer.broker.middlewares.total (測量)
  • moleculer.registry.nodes.total (測量)
  • moleculer.registry.nodes.online.total (測量)
  • moleculer.registry.services.total (測量)
  • moleculer.registry.service.endpoints.total (測量)
  • moleculer.registry.actions.total (測量)
  • moleculer.registry.action.endpoints.total (測量)
  • moleculer.registry.events.total (測量)
  • moleculer.registry.event.endpoints.total (測量)
  • moleculer.request.bulkhead.inflight (測量)
  • moleculer.request.bulkhead.queue.size (測量)
  • moleculer.event.bulkhead.inflight (測量)
  • moleculer.event.bulkhead.queue.size (測量)
  • moleculer.event.received.time (直方圖)
  • moleculer.event.received.error.total(計數)
  • moleculer.event.received.active (測量)
  • moleculer.request.timeout.total (計數)
  • moleculer.request.retry.attempts.total (計數)
  • moleculer.request.fallback.total (計數)
  • moleculer.request.total (計數)
  • moleculer.request.active (測量)
  • moleculer.request.error.total (計數)
  • moleculer.request.time (直方圖)
  • moleculer.request.levels (計數)
  • moleculer.event.emit.total (計數)
  • moleculer.event.broadcast.total (計數)
  • moleculer.event.broadcast-local.total (計數)
  • moleculer.event.received.total (計數)
  • moleculer.transit.publish.total (計數)
  • moleculer.transit.receive.total (計數)
  • moleculer.transit.requests.active (測量)
  • moleculer.transit.streams.send.active (測量)
  • moleculer.transporter.packets.sent.total (計數)
  • moleculer.transporter.packets.sent.bytes (計數)
  • moleculer.transporter.packets.received.total (計數)
  • moleculer.transporter.packets.received.bytes (計數)

客製化

建立新的 metric 註冊

你可以輕易的建立客製化 metrics 。

範例:建立一個計數器

posts.service.js

module.exports = {
    name: "posts",

    actions: {
        // Get posts
        get(ctx) {
            // 遞增 metric
            this.broker.metrics.increment("posts.get.total", 1);

            return this.posts;
        }
    },

    created() {
        // 註冊一個新的計數器 metric
        this.broker.metrics.register({
            type: "counter",
            name: "posts.get.total",
            description: "Number of requests of posts",
            unit: "request",
            rate: true // 設定速率為每分鐘
        });
    }
};

範例:建立帶有標籤的測量

posts.service.js

module.exports = {
    name: "posts",

    actions: {
		// 建立的 Action
        create(ctx) {
            // 更新 metrics
            this.broker.metrics.increment("posts.total", { userID: ctx.params.author }, 1);
            return posts;
        },
		// 刪除的 Action
        remove(ctx) {
            // 更新 metrics
            this.broker.metrics.decrement("posts.total", { userID: ctx.params.author }, 1);
            return posts;
        },

    },

    created() {
        // 註冊一個新的測量 metric
        this.broker.metrics.register({ 
            type: "gauge", 
            name: "posts.total", 
            labelNames: ["userID"]
            description: "Number of posts by user",
            unit: "post"
        });
    }
};

範例:建立包含桶與分位數設定的直方圖

posts.service.js

module.exports = {
    name: "posts",

    actions: {
        // 建立的 Action
        async create(ctx) {
            // 測量建立 post 的時間
            const timeEnd = this.broker.metrics.timer("posts.creation.time");
            const post = await this.adapter.create(ctx.params);
            const duration = timeEnd();

            this.logger.debug("Post created. Elapsed time: ", duration, "ms");
            return post;
        }
    },

    created() {
        // 註冊新的直方圖 metric
        this.broker.metrics.register({
            type: "histogram",
            name: "posts.creation.time",
            description: "Post creation time",
            unit: "millisecond",
            // 產生線性桶值
            linearBuckets: {
                start: 0,
                width: 100,
                count: 10
            },
            quantiles: [0.5, 0.9, 0.95, 0.99],
            maxAgeSeconds: 60,
            ageBuckets: 10
        });
    }
};

參考文獻

[1] Metrics, https://moleculer.services/docs/0.14/metrics.html
[2] Switching to OpenTelemetry from built-in metrics and tracing?, https://github.com/moleculerjs/moleculer/discussions/1125
[3] OpenTelemetry, https://opentelemetry.io/
[4] Tracing Event (legacy), https://moleculer.services/docs/0.14/tracing.html#Event-legacy
[5] Moleculer Console Reporter, https://github.com/moleculerjs/moleculer/blob/master/src/metrics/reporters/console.js
[6] Creating Buckets or Clusters for Numeric Column Values in Exploratory, https://blog.exploratory.io/d04901b32d35
[7] examples/opentelemetry, https://github.com/moleculerjs/moleculer/tree/next/examples/opentelemetry

家家酒小劇場

  • Otter - 什麼是桶與分位數?
  • Boxy - 它是直方圖的參數,可以幫助妳更直觀的了解微服務的狀態,可以參考這篇文章[6] 。

上一篇
Day 19 : 參數驗證
下一篇
Day 21 : Tracing
系列文
Moleculer 家家酒31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言