動態修改 NodeJS 程式中的變數值
如果一個 NodeJS 程式正在執行,有辦法修改程式中的變數值麼?答案是:通過 V8 的 Debugger 介面可以!本文將詳細介紹實現步驟。
啟動一個 HTTP Server
用簡單的 Hello World 做例子吧,不過略作修改。在 global
下放一個變數 message, 然後列印出來:
// message content will be modified !
global.message = "hello world!";
var server = require('http').createServer(function (req, res) {
res.end(global.message);
}).listen(8001);
console.log('pid = %d', process.pid);
用命令啟動 Server
,此時,通過用瀏覽器訪問 http://localhost:8001 可以看到網頁內容是 hello world!
。 接下來我們將嘗試在不改變程式碼,不重啟程式的情況下把 message
換成 "hello bugs!"
。
使 Server 程式進入 Debug 模式
V8 引擎在實現的時候留了 Debugger 介面。 通過命令 node --debug-brk=5858 [filename]
可以啟動一個指令碼,並且立即進入 Debug 模式。
那麼如果是已經執行著的 NodeJS 程式,可以進入 Debug 模式嗎?也是可以的,在作業系統下給 NodeJS 的程式發一個 SIGUSR1
訊號,可以讓程式進入 Debug 模式。 進入 Debug 模式的程式會在本地啟動一個 TCP Server
並且預設監聽5858
埠。
此時在另一個命令列視窗執行命令 node debug localhost:5858
就可以連線到 Debugger 除錯埠, 並且可以使用很多常用的 Debug 命令,比如 c
繼續執行,s
步入, o
步出等。
Debugger 協議
使用node debug hostname:port
命令連線到程式進行 Debug 的方式比較簡單,但是要完成一些高階的功能就會處處受限,因為它只封裝了 Debugger 協議中 command 的一部分。 下面介紹一下這個簡單的協議。
Client 和 Server 的通訊是通過 TCP 進行的。 DebugClient 第一次連線到 DebugServer 的時候會拿到一些 Header,比如 Node 版本, V8 版本等。後面緊跟著一個空的訊息1
訊息1
Type: connect\r\n
V8-Version: 3.28.71.19\r\n
Protocol-Version: 1\r\n
Embedding-Host: node v0.12.4\r\n
Content-Length: 0\r\n
\r\n
訊息實體由 Header 和 Body 組成,訊息1的 Body 為空,所以 Header 中對應的 Content-Length 為 0。而在下面這個例子裡,Body 為一個單行的 JSON 字串,這是由協議所規定的。
訊息2
Content-Length: 46\r\n
\r\n
{"command":"version","type":"request","seq":1}
訊息2的型別( type )是request
,代表這是 Client 發給 Server 的命令,其他的可能值是 response
和 event
分別代表 Server 對 Client 的相應,和 Server 端發生的事件。
訊息3
Content-Length: 137\r\n
\r\n
{"seq":1,"request_seq":1,"type":"response","command":"version","success":true,"body": {"V8Version":"3.28.71.19"},"refs":[],"running":true}
訊息2是 Client 傳送給 Server的,訊息3是 Server 對 Client 的相應,那麼如何判斷訊息3是不是訊息2的結果呢?可以看到訊息2中的 seq
值是1,而 訊息3中的request_seq
值是1。 Debugger 協議正是通過這兩個值把非同步返回的結果和請求一一對應起來的。
Debugger 協議就是這麼的簡單。
例項化一個 Debugger Client
瞭解了 Debugger 協議後,相信好奇心強的程式設計師已經躍躍欲試自己實現一個了。本著不重複發明輪子的原則開始在網上找實現,找了好久找到這個庫 pDebug, 可惜這個庫已經好久不更新了。後來通過閱讀 node-inspector
的原始碼才發現,其實 NodeJS 自帶了一個 Debugger 模組, 相關程式碼在 _debugger 模組裡(原始碼),由於模組名是以 _ 開頭的,所以網上找不到它的 API,好在程式碼註釋寫的非常詳細,很快就能上手。
我們需要的正是這個模組下的 Client, 而 Client 其實是繼承於 Socket 的.
var Client = require('_debugger').Client;
var client = new Client();
client.connect(5858);
client.on('ready', function () {
// 連線成功
});
通過 Debugger 介面執行命令
接下來我們來看看如何修改這個 global 的變數,程式碼如下
function modifyTheMessage(newMessage) {
var msg = {
'command': 'evaluate',
'arguments': {
'expression': 'global.message="' + newMessage + '"',
'global': true
}
};
client.req(msg, function (err, body, res) {
console.log('modified to %s', newMessage);
});
}
client.req
方法封裝了 type=request
訊息型別 和seq
自增的邏輯,因此在構造 msg
JSON物件的時候不需要指明這兩個屬性。 我們要修改 message 其實就是在 JavaScript 呼叫的頂層執行 global.message=newMessage
總結
此時,再訪問http://localhost:8001
可以看到網頁上顯示的內容已經由 'hello world!'
變成了 'hello bugs!'
,是不是很神奇。
這種方式也帶來了很多可能性:
動態修改配置 線上的伺服器不用重啟就可以應用新的配置
模組注入 通過其他任意語言編寫的應用程式為已經執行的 NodeJS 程式注入新的模組
效能監控 可以剝離使用者線上程式碼對第三方效能監控模組的直接依賴
錯誤監控 發生異常時,通過 Debugger 可以抓到發生錯誤的函式和行號,並且抓取各個呼叫棧中的每一個變數,即使是在閉包裡
Chrome 除錯 由於 Chrome 也是基於 V8 的,上述方法也可以用於 Chrome 相關的功能整合
關於
本文相關的原始碼在: https://github.com/wyvernnot/interference_demo;
如果你也對 Debugger 協議感興趣,可以安裝 oneapm-debugger 這個工具,它可以幫助你檢視 Debug 過程中所有實際傳送的資料。 oneapm-debugger
本文系OneAPM工程師編譯整理。OneAPM是中國基礎軟體領域的新興領軍企業,能幫助企業使用者和開發者輕鬆實現:緩慢的程式程式碼和SQL語句的實時抓取。想閱讀更多技術文章,請訪問OneAPM官方技術部落格。
相關文章
- Java 反射修改類的常量值、靜態變數值、屬性值Java反射變數
- jq動態修改select 的option值,使option值自動選中
- SQLServer中動態查詢sql返回值給變數<整理>SQLServer變數
- Go 中的動態作用域變數Go變數
- 遞迴程式中的靜態變數遞迴變數
- Java反射動態修改註解的值Java反射
- PL/SQL變數值可變在程式中會變嗎?SQL變數
- SQL server 動態SQL對變數講行賦值SQLServer變數賦值
- 如何在程式執行時動態修改它的引數(狀態)?
- php中的動態變數的一個應用PHP變數
- 動態引數與靜態引數的判斷、修改
- Java 中賦值類時候修改後原類中的值改變Java賦值
- js中的靜態變數JS變數
- 動態修改input元素type屬性值
- JavaScript如何動態修改img的src屬性值JavaScript
- jQuery動態修改連結的href屬性值jQuery
- MySQL中變數的定義和變數的賦值使用MySql變數賦值
- 聊聊 Vue 中 title 的動態修改Vue
- 繫結變數在靜態sql和動態sql中變數SQL
- ant如何替換JAVA程式碼中的String變數中的值?Java變數
- JS建立動態的變數名JS變數
- Oracle動態、靜態引數引數修改規則Oracle
- 物件呼叫動態變數物件變數
- shell中變數的取值與賦值變數賦值
- 再說mysql中的變數賦值MySql變數賦值
- Shell程式設計-04-Shell中變數數值計算程式設計變數
- 在程式中定義多個同值不同名的變數變數
- 動態修改Shape的solid屬性的color值Solid
- 動態監控input的值的變化 賦值value觸發賦值
- 動態監聽輸入框值的變化
- Java靜態變數在靜態方法內部無法改變值Java變數
- Spring如何為靜態變數注入值Spring變數
- JS中的變數賦值深入理解JS變數賦值
- 自己對Java中if變數賦值的理解Java變數賦值
- Windows批處理中的變數和值Windows變數
- js修改css變數值實現主題切換JSCSS變數
- Linux中修改環境變數PATH的方法Linux變數
- 檢視引數是否可以動態修改