сейчасNode.jsВсе больше и больше популярных, все больше и больше сценариев приложений, научитесь эффективно отлаживатьNode.jsЭто сделает ежедневную разработку более эффективной. Используйте следующееinspectorотладкаnodejsпрограмма
Node6.3+версия предоставляет два протокола для отладки:v8 Debugger Protocolа такжеv8 Inspector Protocolможно использовать сторонниеClient/IDEДождитесь мониторинга и вмешательства в запущенный процесс Node (v8) для отладки.
v8 Inspector Protocolэто недавно добавленный протокол отладки, который передается черезwebsocket(обычно используется порт 9229) сClient/IDEвзаимодействие, основанное наChrome/ChromiumбраузерdevtoolsПредоставляет графический интерфейс отладки.
1 Включить отладку
1.1 Код сервера отладки
Если ваш скрипт строитсяhttpилиnetсервер, вы можете напрямую использовать--inspect
const Koa = require('koa')
const app = new Koa()
app.use(async ctx => {
let a = 0
const longCall = () => {
while (a < 10e8) {
a++
}
}
longCall()
ctx.body = `Hello ${a}`
})
app.listen(3000, () => {
console.log('程序监听了3000端口')
})
использоватьnode --inspect=9229 app.jsзапустите свой скрипт,9229указанный номер порта
# 控制台会输出如下:
/usr/local/bin/node --inspect=9229 src/inspector/demo.js
Debugger listening on ws://127.0.0.1:9229/c4f1e345-e811-47a2-b44a-65f68c0c2cc3
Debugger attached.
# 可以在浏览器里打开:http://127.0.0.1:9229/json 看到一些信息, c4f1e345-e811-47a2-b44a-65f68c0c2cc3 为uuid,不同调试面板的uuid来区分;
--inspectДля общей программы она мигает, и выполнение завершается до отправки сигнала точки останова. Точки останова вообще не работают, вы можете--inspect-brk;
1.2 Код скрипта отладки
Если вы завершите процесс сразу после запуска вашего скрипта, вам нужно использовать--inspect-brkДля запуска отладчика, чтобы скрипт мог сломаться до выполнения кода, иначе весь код догоняет сразу до конца кода, завершая процесс, и отладка вообще невозможна.
node --inspect-brk=9229 app.js
2 Доступ к инструменту отладки
2.1 VS Code
Vs CodeвстроенныйNode debugger,служба поддержкиv8 Debugger Protocolа такжеv8 Inspector ProtocolДва протокола. дляv8 Inspector Protocol, просто добавьте строку в конфигурациюAttachТип конфигурации
существуетDebugпанель управления, нажмитеsettingsзначок, открыть.vscode/launch.json.
Нажмите «Node.js» для первоначальной настройки.
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/app.js"
}
]
}
2.2 Chrome DevTools
- Способ 1: открыть в браузере Chrome
chrome://inspectнажмитеConfigureкнопку, убедитесь, что хост и порт есть в списке.
- Способ 2: с вышеуказанного хоста и порта
/json/listкопироватьdevtoolsFrontendUrlили–-inspectзапросите информацию и скопируйте в Chrome.
2.2.1 Console Panel
chromeдоступ к отладкеnodeПосле процедуры вы можетеConsoleагентNodeВесь вывод консоли в процессе обеспечивает гибкую функцию фильтрации Filter, а также может выполнять код непосредственно в контексте кода процесса Node.
2.2.2 Sources Panel
SourcesВсе загруженные скрипты можно посмотреть в , включая сторонние библиотеки иNodeОсновная библиотека, выбранные файлы можно редактировать,Ctrl + CСохранение может напрямую изменить работающий скрипт.
2.2.3 Profile Panel
ProfileДля мониторинга производительности запущенных скриптов, включая использование ЦП и памяти,CPU profile, который может записывать время процессора, занятое выполнением функций Javascript, на временной шкале.
Существует два типа временных периодов записи профиля.
- Ручной запуск/остановка: нажмите
startЧтобы начать запись, нажмитеstopостановить запись - Вставьте запуск/остановку вызовов API в код
console.profile('tag')console.profileEnd('tag'), допустимыйSourcesОтредактируйте и сохраните код прямо в панели, а затем обновите его, нажав F5.
профиль имеет три вида
- chart: широко известный как график пламени, он отображает стек вызовов функций со временем в качестве горизонтальной оси. Следующий простой пример анализа
tick,Одинtickдолжно бытьNodeВызывается из нижнего слоя, используется в узлеprocess.nextTick(fn)а такжеsetTimeout(fn, deloy)Системный обратный вызов будет генерировать новыеtick, соответствующий созданию нового стека вызовов.
Функции вызываются в порядке от нижней части стека к вершине стека. Первый стек на рисунке вышеparserOnHeadersCompleteвызывается нижним слоем,parserOnHeadersCompleteназывается вparserOnIncoming,parserOnIncomingназывается вemit...И так далее.
Ширина стека вызовов — это время, необходимое для выполнения функции. Время выполнения функции включает в себя время выполнения ее внутренних вызовов других функций, поэтому время вызова функции, расположенной относительно близко к низу стека, должно быть больше, чем время вызова функции, близкой к вершине стека. . Исключая время выполнения внутренних вызовов других функций, это время выполнения текущей функции.
Нажмите на функцию, чтобы перейти кSourcesРасположение определения функции на панели.
-
Name: Имя функции. -
Self time: время, необходимое для завершения текущего вызова функции, включая только объявление самой функции, а не какие-либо функции, вызываемые функцией. -
Total time: время, необходимое для завершения текущего вызова этой функции и любых функций, которые она вызывает. -
URL: расположение определения функции в формате file.js:100 , где file.js — это имя файла, в котором определена функция, а 100 — номер строки определения. -
Aggregated self time: общее время всех вызовов функции в записи, исключая функции, вызываемые этой функцией. -
Aggregated total time: общее время всех вызовов функции, за исключением функций, вызываемых этой функцией. -
Not optimized: Если анализатор обнаружил возможную оптимизацию функции, она будет указана здесь. -
heavy(Bottom Up): Статистика снизу вверх, снизу относится к нижней части графика пламени.
- tree(Top Down): Статистика сверху вниз, сверху относится к вершине графика пламени.
Видно, что большая часть времени программы уходит наlongCallПри вызове этой функции;
2.2.4 Memory profile
Профилировщик кучи может отображать распределение памяти по объектам JavaScript страницы и связанным узлам DOM (см. также Дерево хранения объектов). С помощью анализатора можно стрелятьJS 堆快照,分析内存图,比较快照так же как查找内存泄漏.
3. Реализация прокси-сервера Node Inspector
пройти черезnode inspectorОтладка точки останова — очень распространенный метод отладки. Но было несколько проблем с предыдущей отладкой, которые сделали нашу отладку менее эффективной.
- существует
vscodeотладка, вinspectorизменение порта илиwebsocket idПовторно подключитесь после изменения. - существует
devtoolsотладка, вinspectorизменение порта илиwebsocket idПовторно подключитесь после изменения.
Как инспектор узлов решает две вышеупомянутые проблемы?
По первому вопросу вvscode, он будет называть себя/jsonинтерфейс для получения последнихwebsocket id, затем используйте новыйwebsocket idСоединен сnode inspectorоказание услуг. Таким образом, решение состоит в том, чтобы реализоватьtcpФункция прокси может выполнять пересылку данных.
По второму вопросу, посколькуdevtoolsне будет автоматически приобретать новыеwebsocket id, поэтому нам нужно сделать динамическую замену, поэтому решение состоит в том, чтобы проксировать службу на/jsonПолучатьwebsocket id, затем вwebsocketпри рукопожатииwebsocket idДелайте динамические замены в заголовках запросов.
Нарисуйте блок-схему:
3.1 TCP-прокси
Во-первых, реализоватьtcpФункция агента на самом деле очень проста, т. е. черезnodeизnetмодуль для создания прокси-портаTcp Server, а затем, когда есть соединение, создайте соединение с целевым портом, после чего данные могут быть перенаправлены.
Простая реализация выглядит следующим образом:
const net = require('net');
const proxyPort = 9229;
const forwardPort = 5858;
net.createServer(client => {
const server = net.connect({
host: '127.0.0.1',
port: forwardPort,
}, () => {
client.pipe(server).pipe(client);
});
// 如果真要应用到业务中,还得监听一下错误/关闭事件,在连接关闭时即时销毁创建的 socket。
}).listen(proxyPort);
Вышеприведенное реализует относительно простую прокси-службу черезpipeМетод связывает данные двух сервисов.clientКогда данные будут доступны, они будут отправлены наserverсередина,serverКогда есть данные, они также будут отправлены наclientсередина.
когда это будет сделаноTcpПосле функции прокси ее уже можно реализоватьvscodeпотребности в отладкеvscodeниже среднегоlaunch.jsonУказанный порт является портом прокси вconfigurationsдобавить конфигурацию
{
"type": "node",
"request": "attach",
"name": "Attach",
"protocol": "inspector",
"restart": true,
"port": 9229
}
Затем, когда приложение перезапустится, или заменитеinspectпорт,vscodeможет автоматически повторно передать порт проксиattachк вашему приложению.
3.2 Получить идентификатор веб-сокета
В начале этого шага необходимо решитьdevtoolsможно переустановить без изменения ссылкиattachПроблема в том, что при запускеnode inspector serverкогда,inspectorСервис также предоставляет/jsonизhttpинтерфейс для полученияwebsocket id.
Это довольно просто, просто отправьте его напрямуюhttpзапрос к порту назначения/json, вы можете получить данные:
[ { description: 'node.js instance',
devtoolsFrontendUrl: '...',
faviconUrl: 'https://nodejs.org/static/favicon.ico',
id: 'e7ef6313-1ce0-4b07-b690-d3cf5274d8b0',
title: '/Users/wanghx/Workspace/larva-team/vscode-log/index.js',
type: 'node',
url: 'file:///Users/wanghx/Workspace/larva-team/vscode-log/index.js',
webSocketDebuggerUrl: 'ws://127.0.0.1:5858/e7ef6313-1ce0-4b07-b690-d3cf5274d8b0' } ]
Поле id в приведенных выше данных — это то, что нам нужноwebsocket id.
3.3 Агент-инспектор
понятноwebsocket idПосле этого вы можетеtcpделать через проксиwebsocket idДинамическая замена , сначала нам нужна фиксированная ссылка, поэтому сначала установите прокси-ссылку, например, мой порт прокси-сервиса9229, тогда прокси-ссылка для chrome devtools:
chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/__ws_proxy__
кроме последнегоws=127.0.0.1:9229/__ws_proxy__Остальные зафиксированы, а последний сразу видно, что онwebsocketссылка на. в__ws_proxy__Он используется для занятия пространства, используется вchrome devtoolsИнициировать эту прокси-ссылкуwebsocketКогда делается запрос на рукопожатие,__ws_proxy__заменитьwebsocket idзатем впередnodeизinspectorоказание услуг.
к вышеизложенномуtcpчерез проксиpipeЛогический код можно немного изменить.
const through = require('through2')
client
.pipe(through.obj((chunk, enc, done) => {
if (chunk[0] === 0x47 && chunk[1] === 0x45 && chunk[2] === 0x54) {
const content = chunk.toString();
if (content.includes('__ws_proxy__')) {
return done(null, Buffer.from(content.replace('__ws_proxy__', websocketId)));
}
}
done(null, chunk);
}))
.pipe(server)
.pipe(client)
пройти черезthrough2Создаватьtransformstream для внесения изменений в передаваемые данные.
Просто судитьchunkПервые три байтаGET,еслиGETуказать, что это может бытьhttpпросьба, которая может бытьwebsocketзапрос на обновление протокола. Печать заголовков запроса выглядит так:
GET /__ws_proxy__ HTTP/1.1
Host: 127.0.0.1:9229
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: chrome-devtools://devtools
Sec-WebSocket-Version: 13
затем поместите в него путь/__ws_proxyзаменить на соответствующийwebsocketId, затем впередnodeизinspector serverна завершениеwebsocketрукопожатие, следующийwebsocketКоммуникация не требует обработки данных, просто пересылает их напрямую.
Далее, даже если перезапустить различные приложения или заменитьinspectorпорты не требуют заменыdebugссылка, просто пере-inspector serverПри перезапуске во всплывающем окне ниже
Один щелчок Reconnect DevTools восстановит отладку.
Ссылаться на:Реализация прокси-сервера Node Inspector