成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

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

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

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

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

};

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

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 模塊收集到兩個數據,分布是 server 處理請求的耗時和 client 經歷的耗時。

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

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

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 中再記錄操作的結束時間并合并上下文,最后通過 perf_hooks 機制通知開發者。另外只有在開發者注冊了 perf_hooks 的觀察者時才會執行這些邏輯,這樣對不開啟該功能的開發者就不會有性能的損耗。有了這兩個方式后,收集數據就變得簡單,只需要找到開始和結束的點,加入對應的函數就行。

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

}

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

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 目前已經被合到主干,下個版本應該就可以使用。

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

這個 PR 一開始不是很想提,同步 API 很多,而我要做的事情就是在這些 API 開始和結束的時候記錄具體的時間,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 也不是為了提而提,我們經常收到業務反饋說事件循環延遲告警,但是不知道具體什么原因。我們很容易能監控到事件循環延遲,但是卻很難知道具體的原因,因為情況太多了。而同步文件操作就是其中一種情況,所以我們希望收集這個數據。但是我們發現在開發者層面很難做到這個事情。如果我是業務同學,那么我需要在每個同步 API 的前面加統計代碼,如果我是監控 SDK 提供者,那么我只能劫持文件模塊的 API。無論哪種方式都不是優雅的解決方案。但是如何在 Node.js 內核支持的話,情況就不一樣了,雖然只是簡單地把代碼移到 Node.js 里面,但是卻能很好地解決問題,每次用戶調用同步 API 的時候,無論是業務同學還是監控 SDK 提供者都能非常簡單地通過 perf_hooks 機制拿到這些 API 的耗時數據。所以我回復 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 目前實現了 trace_events 模塊,里面有部分 trace 數據是通過 V8 提供的 trace API 實現的,但是 Node.js 目前沒有暴露出來,我做的事情就是把這個 API 暴露出來,這樣的好處是開發者可以利用 V8 的 trace 機制產生 trace 數據,并且可以通過 Node.js 的 trace_events 模塊和 inspector 模塊收集這些數據。因為我之前開發了一個 tracepoint 的庫來做這個事情,但是相比來說在 Node.js 里支持會好很多。但是 Node.js 同學說在使用這個 API 時和 V8 團隊達成了協議不會對外暴露給開發者,需要 tsc 討論這個事情,下面是回復。

  • 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 會討論這個事情,希望能支持這個功能。

總結

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

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

責任編輯:武曉燕 來源: heanarkh
相關推薦

2021-09-26 05:06:04

Node.js模塊機制

2021-11-06 18:40:27

js底層模塊

2022-03-26 16:51:27

Node.jstrace架構

2021-09-26 22:22:42

js模塊Node

2021-10-22 08:29:14

JavaScript事件循環

2015-03-10 10:59:18

Node.js開發指南基礎介紹

2013-11-01 09:34:56

Node.js技術

2021-12-25 22:29:57

Node.js 微任務處理事件循環

2012-02-03 09:25:39

Node.js

2020-05-29 15:33:28

Node.js框架JavaScript

2011-11-10 08:55:00

Node.js

2012-10-24 14:56:30

IBMdw

2011-09-08 13:46:14

node.js

2011-11-01 10:30:36

Node.js

2011-09-02 14:47:48

Node

2011-09-09 14:23:13

Node.js

2011-11-02 09:04:15

Node.js

2020-10-26 08:34:13

Node.jsCORS前端

2021-04-06 10:15:29

Node.jsHooks前端

2024-07-08 08:53:52

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩在线免费视频 | 国产一区2区 | 免费看91 | 日韩一二三区视频 | 欧美激情一区 | 成年人在线 | 成人亚洲网站 | 亚洲欧美视频一区 | 盗摄精品av一区二区三区 | 成人国产精品久久 | 久久综合九色综合欧美狠狠 | 亚洲精品日韩视频 | 国产欧美在线 | 99久久久久 | 亚洲欧美中文日韩在线v日本 | 成人福利在线 | 久久久久久亚洲精品 | 日日精品| 男人的天堂久久 | 国产精品久久久久久久久大全 | 岛国av免费观看 | 欧美一区二区精品 | 久久国产精品一区二区三区 | 91精品国产乱码久久久久久久久 | 国产精品九九九 | 午夜免费视频 | 久久久久无码国产精品一区 | 一区二区三区四区在线 | 一区影视| 国产日韩一区二区 | 精品美女久久久 | 91精品国产乱码久久久久久久久 | 一级美国黄色片 | 日韩电影免费在线观看中文字幕 | 黄色大片观看 | 成人国产免费视频 | 综合一区 | 色av一区| 99久久精品国产一区二区三区 | 午夜电影一区二区 | 99久久免费精品国产免费高清 |