自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊聊最近給 Node.js 提交的幾個PR

開發(fā) 前端
Node.js 在 12.7.0 版本里已經(jīng)支持收集 HTTP Server 處理請求的耗時。在這個基礎(chǔ)上,我做的事情主要是支持收集發(fā)送一個 HTTP Request 到收到響應(yīng)所需要的耗時。

最近因為工作中碰到的一些問題,希望給 Node.js 提交一些代碼來解決我碰到的問題,一共提交了 4 個 PR,目前一個已經(jīng)在 17.8.0 中發(fā)布,一個剛合到主干,一個等 reviewer 回復(fù),一個等 31 號 tsc 開會討論??偟膩碚f,提交的代碼并不復(fù)雜,但是的確解決了我的問題,同時我覺得也是開發(fā)者需要的一些功能。下面介紹一下這幾個 PR 做的事情。

1.通過 perf_hooks 收集 HTTP 模塊的耗時

了解 HTTP Server 處理一個請求的耗時和發(fā)送一個 HTTP Request 到收到響應(yīng)所需要的耗時是很多開發(fā)者都需要的,這些數(shù)據(jù)可以幫助我們了解我們服務(wù)的性能和網(wǎng)絡(luò)鏈路的情況。沒有 Node.js 的支持,開發(fā)者如果想收集這個數(shù)據(jù)會非常麻煩,每個開發(fā)者都需要實現(xiàn)開始請求前,記錄開始時間,拿到響應(yīng)后記錄結(jié)束時間這些入侵業(yè)務(wù)邏輯的重復(fù)代碼,而 SDK 的提供者,則需要劫持 http 模塊來實現(xiàn)這樣的功能。Node.js 在 12.7.0 版本里已經(jīng)支持收集 HTTP Server 處理請求的耗時。在這個基礎(chǔ)上,我做的事情主要是支持收集發(fā)送一個 HTTP Request 到收到響應(yīng)所需要的耗時。具體實現(xiàn)如下。

ClientRequest.prototype._finish = function _finish() {
if (hasObserver('http')) {
this[kClientRequestStatistics] = {
startTime: process.hrtime(),
type: 'HttpClient',
};
}

};

首先在請求發(fā)送完畢后開始計時,如果開發(fā)者在通過 perf_hooks 收集 http 模塊的數(shù)據(jù),那么就開始記錄請求的開始時間。然后收到 HTTP 響應(yīng)并解析完請求行和請求頭時記錄結(jié)束時間。

function parserOnIncomingClient(res, shouldKeepAlive) {
emitStatistics(req[kClientRequestStatistics]);

}


function emitStatistics(statistics) {
if (!hasObserver('http') || statistics == null) return;
const startTime = statistics.startTime;
const diff = process.hrtime(startTime);
const entry = new InternalPerformanceEntry(
statistics.type,
'http',
startTime[0] * 1000 + startTime[1] / 1e6,
diff[0] * 1000 + diff[1] / 1e6,
undefined,
);
enqueue(entry);

}

最后通過 perf_hooks 機制通知用戶。接下來通過一個例子看看如何使用。

const { PerformanceObserver } = require('perf_hooks');

const http = require('http');

const obs = new PerformanceObserver((items) => {

items.getEntries().forEach((item) => {
console.log(item);
});

});

obs.observe({ entryTypes: ['http'] });



const PORT = 8080;

http.createServer((req, res) => {
res.end('ok');}).listen(PORT, () => {
http.get(`http://127.0.0.1:${PORT}`);

});

在上面的例子中我們可以通過 perf_hooks 模塊收集到兩個數(shù)據(jù),分布是 server 處理請求的耗時和 client 經(jīng)歷的耗時。

2.通過 perf_hooks 收集 TCP 連接和 DNS 解析的耗時

第一個 PR 合進去后,我覺得后續(xù)應(yīng)該有很多地方可以通過 perf_hooks 機制進行數(shù)據(jù)的收集。接下來做的事情就是通過 perf_hooks 收集 TCP 連接和 DNS 解析的耗時。做性能分析和監(jiān)控的時候,這部分的數(shù)據(jù)也是開發(fā)者感興趣的。這次實現(xiàn)了兩個通用的方式來處理一些公共的邏輯,希望后續(xù)其他地方收集數(shù)據(jù)的時候也可以復(fù)用。

function startPerf(target, key, context = {}) {
if (hasObserver(context.type)) {
target[key] = {
...context,
startTime: process.hrtime(),
};
}

}


function stopPerf(target, key, context = {}) {
const ctx = target[key];
if (ctx && hasObserver(ctx.type)) {
const startTime = ctx.startTime;
const diff = process.hrtime(startTime);
const entry = new InternalPerformanceEntry(
ctx.name,
ctx.type,
startTime[0] * 1000 + startTime[1] / 1e6,
diff[0] * 1000 + diff[1] / 1e6,
{ ...ctx.detail, ...context.detail },
);
enqueue(entry);
}

}

這兩個方式的邏輯很簡單,startPerf 是記錄操作的開始時間,并且記錄一些上下文,然后在 stopPerf 中再記錄操作的結(jié)束時間并合并上下文,最后通過 perf_hooks 機制通知開發(fā)者。另外只有在開發(fā)者注冊了 perf_hooks 的觀察者時才會執(zhí)行這些邏輯,這樣對不開啟該功能的開發(fā)者就不會有性能的損耗。有了這兩個方式后,收集數(shù)據(jù)就變得簡單,只需要找到開始和結(jié)束的點,加入對應(yīng)的函數(shù)就行。

function internalConnect() {
if (addressType === 6 || addressType === 4) {
startPerf(self, kPerfHooksNetConnectContext, { type: 'net', name: 'connect', detail: { host: address, port } });
}

}

internalConnect 是發(fā)起 TCP 請求的函數(shù),目前只收集 TCP 連接的耗時,我們知道 net 模塊還包括了 IPC 的實現(xiàn),但是 IPC 是基于本機的,連接的耗時通常很快,收集這個數(shù)據(jù)沒有太大意義。另外只有連接成功的時候才會被收集。具體在 afterConnect 函數(shù)。

function afterConnect(status, handle, req, readable, writable) {
if (status === 0) {
stopPerf(self, kPerfHooksNetConnectContext);
}

}

接下來看一個使用的例子。

const { PerformanceObserver } = require('perf_hooks');

const net = require('net');

const obs = new PerformanceObserver((items) => {

items.getEntries().forEach((item) => {
console.log(item);
});

});

obs.observe({ entryTypes: ['net'] });

const PORT = 8080;

net.createServer((socket) => {
socket.destroy();}).listen(PORT, () => {
net.connect(PORT);

});

通過上面的代碼,我們就可以收集到 TCP 連接的耗時。這個 PR 中除了支持收集 TCP 耗時,還支持了 DNS 解析的耗時,包括 Promise 化的 API。原理類似,不再具體介紹,看一個使用例子就可以。

const { PerformanceObserver } = require('perf_hooks');

const dns = require('dns');

const obs = new PerformanceObserver((items) => {

items.getEntries().forEach((item) => {
console.log(item);
});

});

obs.observe({ entryTypes: ['dns'] });
dns.lookup('localhost', () => {});
dns.promises.resolve('localhost');

這個 PR 目前已經(jīng)被合到主干,下個版本應(yīng)該就可以使用。

3.通過 perf_hooks 收集同步文件 API 耗時

這個 PR 一開始不是很想提,同步 API 很多,而我要做的事情就是在這些 API 開始和結(jié)束的時候記錄具體的時間,reviewer 也提了這個問題。

  • While I have no specific objection to this, I do wonder what this provides that wrapping the sync calls in performance.timerify wouldn't provide?

不過這個 PR 也不是為了提而提,我們經(jīng)常收到業(yè)務(wù)反饋說事件循環(huán)延遲告警,但是不知道具體什么原因。我們很容易能監(jiān)控到事件循環(huán)延遲,但是卻很難知道具體的原因,因為情況太多了。而同步文件操作就是其中一種情況,所以我們希望收集這個數(shù)據(jù)。但是我們發(fā)現(xiàn)在開發(fā)者層面很難做到這個事情。如果我是業(yè)務(wù)同學(xué),那么我需要在每個同步 API 的前面加統(tǒng)計代碼,如果我是監(jiān)控 SDK 提供者,那么我只能劫持文件模塊的 API。無論哪種方式都不是優(yōu)雅的解決方案。但是如何在 Node.js 內(nèi)核支持的話,情況就不一樣了,雖然只是簡單地把代碼移到 Node.js 里面,但是卻能很好地解決問題,每次用戶調(diào)用同步 API 的時候,無論是業(yè)務(wù)同學(xué)還是監(jiān)控 SDK 提供者都能非常簡單地通過 perf_hooks 機制拿到這些 API 的耗時數(shù)據(jù)。所以我回復(fù) reviewer 我提這個 PR 的理由。

  • I think if Node.js core provide this, user just need to new a observer by perf_hooks for collecting the cost time of fs sync api. otherwise, all users need to write some same code to do this. And if i want to provide a sdk to do this, i need to hijack the fs api.

使用方式也非常簡單。

const { PerformanceObserver } = require('perf_hooks');

const fs = require('fs');

const obs = new PerformanceObserver((items) => {

items.getEntries().forEach((item) => {
console.log(item);
});

});

obs.observe({ entryTypes: ['fs'] });
fs.readFileSync(__filename);

這樣我們就可以知道 readFileSync 的耗時。但是這個 PR 目前還沒有 approve。我還是希望 Node.js 能支持這個能力。

4.暴露 V8 的 trace API

Node.js 目前實現(xiàn)了 trace_events 模塊,里面有部分 trace 數(shù)據(jù)是通過 V8 提供的 trace API 實現(xiàn)的,但是 Node.js 目前沒有暴露出來,我做的事情就是把這個 API 暴露出來,這樣的好處是開發(fā)者可以利用 V8 的 trace 機制產(chǎn)生 trace 數(shù)據(jù),并且可以通過 Node.js 的 trace_events 模塊和 inspector 模塊收集這些數(shù)據(jù)。因為我之前開發(fā)了一個 tracepoint 的庫來做這個事情,但是相比來說在 Node.js 里支持會好很多。但是 Node.js 同學(xué)說在使用這個 API 時和 V8 團隊達成了協(xié)議不會對外暴露給開發(fā)者,需要 tsc 討論這個事情,下面是回復(fù)。

  • Not sure we should do this. I mean, I generally prefer this, but when we added that trace API it was with the agreement with the v8 team that we would not expose a public API for it since they might still want to make changes to the underlying mechanism and exposing the public apis would make that more difficult.

最近 tsc 會討論這個事情,希望能支持這個功能。

總結(jié)

給大型的開源項目提交 PR 是一個很 cool 但是其實也挺不容易的過程。首先需要了解提 PR 的整個流程,接著需要去了解相關(guān)模塊的具體實現(xiàn)邏輯,比如我最近把 trace 模塊和 perf_hooks 模塊研究了一遍,然后才能確定怎么修改代碼,修改完之后還需要寫對應(yīng)的測試,以及不能影響其他邏輯,都完成后還需要按照 reviewer 的建議進行反復(fù)修改,你的想法能得到 reviewer 的同意也不容易,比如我之前也提過 keepalive 和 so_resueport 的 PR,不過因為平臺兼容性的問題沒有被合進去,因為 Libuv 定位的是跨平臺的庫。但是,如果你提交成功一次,了解了整個過程之后,以后就會節(jié)約很多時間。最后附上四個 PR 的地址,有興趣的同學(xué)也可以看下。

https://github.com/nodejs/node/pull/42345

https://github.com/nodejs/node/pull/42390

https://github.com/nodejs/node/pull/42421

https://github.com/nodejs/node/pull/42462

責(zé)任編輯:武曉燕 來源: heanarkh
相關(guān)推薦

2021-09-26 05:06:04

Node.js模塊機制

2021-11-06 18:40:27

js底層模塊

2022-03-26 16:51:27

Node.jstrace架構(gòu)

2021-09-26 22:22:42

js模塊Node

2013-11-01 09:34:56

Node.js技術(shù)

2015-03-10 10:59:18

Node.js開發(fā)指南基礎(chǔ)介紹

2021-10-22 08:29:14

JavaScript事件循環(huán)

2020-05-29 15:33:28

Node.js框架JavaScript

2012-02-03 09:25:39

Node.js

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

2011-09-09 14:23:13

Node.js

2011-11-01 10:30:36

Node.js

2011-09-08 13:46:14

node.js

2011-09-02 14:47:48

Node

2012-10-24 14:56:30

IBMdw

2011-11-10 08:55:00

Node.js

2011-11-02 09:04:15

Node.js

2019-07-09 14:50:15

Node.js前端工具

2015-06-23 15:27:53

HproseNode.js

2020-10-26 08:34:13

Node.jsCORS前端
點贊
收藏

51CTO技術(shù)棧公眾號