iT邦幫忙

2024 iThome 鐵人賽

DAY 22
1
DevOps

全端監控技術筆記---從Sentry到Opentelemetry系列 第 22

Day22--手寫一個獲取 metric data 的 SDK

  • 分享至 

  • xImage
  •  

前言

在前一篇,我們了解 Opentelemetry node sdk 使用 HttpInstrumentation 來攔截 http 請求,透過 meter 來創建Histogram物件,蒐集 duration 這個metric data;然後透過 PrometheusExporter 來創建 Http server,讓 Prometheus 服務來拉取資料。

接下來我們來一一實現吧!

有關 Meter 和 Histogram

Histogram

在這次demo中,我們就簡單蒐集 http duraction 的總和指標。因此在 Histogram 物件中,主要就是一個sum屬性來存放持續時間、以及和record函數來記錄、更新 sum value:

class MockHistogram {
    constructor(name, options) {
        this.name = name;
        this.sum = 0;
    }
    // 記錄數據
    record(value) {
        this.sum += value;
    }
}

Meter

透過上一篇的分析,我們知道 Meter 是用來創建並管理多個 Histogram 。 在這次 demo 中,我們利用 createHistogram 創建新的 Histogram 物件,並用 _histograms 數組來存儲應用中的 Histogram 物件,同時透過 getHistogram 暴露出去

createHistogram(name, options) {
    const histogram = new MockHistogram(name, options);
    this._histograms.push(histogram);
    return histogram;
}

 getHistogram() {
    return this._histograms;
}

http攔截

跟 trace data 一樣,我們要來進行 http 攔截;不過不同於之前寫的 express middleware,這次我們嘗試直接使用 http 本身來進行服務端請求的攔截,所以發起請求接收請求都會在 http instrumentation 中完成。

攔截發送請求

利用覆蓋 http.request = callback來進行攔截`

http.request = function (...args) {
    const req = originalHttpRequest.apply(this, args);
    req.on('response', (res) => {
        // 採集邏輯
    });
    return req;
};

攔截發起請求

我們可以覆寫掉 http.Server.prototype.emit = callback來進行攔截:

const originalEmit = http.Server.prototype.emit;
    http.Server.prototype.emit = function (event, ...args) {
        if (event === 'request') {
            // 採集邏輯
        }
        return originalEmit.apply(this, [event, ...args]);
    };

採集 duration metric data

首先,在Opentelemerty js中,計算時間會用到 process.hrtime() ,而不是 Date() ,主要是因為精確度的要求。Date 的精確度是到毫秒級(millisecond),而 hrtime 的精確度是到納秒級別(nanosecond)。所以,我們在計算 duration 的時候,流程會是這樣:

  1. 定義開始時間---const startTime = process.hrtime()
  2. 執行完主要程式邏輯後,再獲取結束時間---const endTime = process.hrtime()
  3. 利用endTimestartTime來計算 duration

然後把計算邏輯寫在攔截 http 的 callback 中

記錄 Histogram

HttpInstrumentation中,我們可以先為 server http 和 client http 分別創建 Histogram 物件:

    this._httpServerDurationHistogram = this.meter.createHistogram(
        'http.server.duration',
        {
            description: 'Measures the duration of inbound HTTP requests.',
            unit: 'ms',
        },
    );
    this._httpClientDurationHistogram = this.meter.createHistogram(
        'http.client.duration',
        {
            description: 'Measures the duration of outbound HTTP requests.',
            unit: 'ms',
        },
    );

然後分別實現兩者對request/response 持續時間的採集邏輯 --- recordServerRequestDurationrecordClientRequestDuration

接著就可以在攔截的callback中使用了~ 完整程式碼可以參看 link

Mock Promethues Exporter

這個部分就比較簡單了,在初始化的時候就創建一個 web server,在/metrics endpoint 暴露從 meter 蒐集並且格式化後的 metric data:

...
    init() {
        const server = http.createServer((req, res) => {
            if (req.url === '/metrics') {
                
                res.writeHead(200, { 'Content-Type': 'text/plain' });
                let text = '';
                //格式化 metric data                   
                res.end(text);
            } else {
                res.writeHead(404, { 'Content-Type': 'text/plain' });
                res.end('Not Found');
            }
        });

        server.listen(9469, () => {
            console.log('Metrics available at http://localhost:9469/metrics');
        });
    }
...

查看運行結果

我們在這個 Node.js demo 中新增了一個 API,它會向另一個 Python 服務發送一個請求:

app.get('/demo', async (req, res) => {
    const data = await axios.get(`${PYTHON_SERVICE_URL}/api/demo-01`);
    res.send('hello server');
});

運行應用後,發送一次 API 請求,然後打開 /metrics endpoint,即可看到成功輸出的 HTTP 請求持續時間數據:

image

小結

透過上述的 demo,我們自己實現了自己採集 HTTP 請求延遲的 metric data,並且模擬 Prometheus Exporter 那樣把 metric
data 暴露到 /metric endpoint。

完整程式碼可以在此 Github repository中查看。

ref

ChangeLog

  • 20241006--補上圖片與小結
  • 20240927--初稿

上一篇
Day21--Opentelemetry是如何獲取metric data的?
下一篇
Day23--簡單demo看看 Opentelemetry log data + Loki + Grafana
系列文
全端監控技術筆記---從Sentry到Opentelemetry30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言