node环境编译node-curl说明

指定 openssl 版本

bind.gyp 文件中,增加

1
2
3
4
"defines": [
"OPENSSL_API_COMPAT=0x10100001L",
"OPENSSL_CONFIGURED_API=30000",
]

指定 libcurl 版本

LIBCURL_RELEASE=7.78.0 PUBLISH_BINARY="false" sh ./scripts/ci/build.sh

node中gyp文档

GYP 英文文档

1
2
3
4
GYP - https://gyp.gsrc.io/docs/InputFormatReference.md#Predefined-Variables
node's common.gypi - https://github.com/nodejs/node/blob/master/common.gypi
node-gyp's addon.gypi - https://github.com/nodejs/node-gyp/blob/master/addon.gypi
variables node-gyp adds at run time - https://github.com/nodejs/node-gyp/blob/master/lib/configure.js#L292

中文文档

1
http://lyuzhao.com/2017/12/25/Gyp-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/

nodejs绑定C++库

工具链

  1. nodejs
  2. node-gyp
    https://github.com/nodejs/node-addon-api/blob/main/doc/setup.md

调试

  1. Cmd+Shift+P 输入 configure task 配置一个任务,该任务会执行 npm run rebuild:dev,生成带调试信息的 node 扩展文件。笔者的配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "dev",
"group": "rebuild",
"problemMatcher": [],
"label": "npm: dev",
"detail": "node-gyp configure build --debug"
}
]
}
  1. 点击 debug 按钮之后,下面在 launch.json 中配置调试 node 扩展的任务,注意在配置的时候增加一个 preLaunchTask 任务,该任务就是我们上一步配置的。最终 luanch.json 配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"version": "0.2.0",
"configurations": [{
"type": "lldb",
"request": "launch",
"name": "Debug",
"preLaunchTask": "npm: dev",
"program": "/Users/huangzz/.nvm/versions/node/v18.0.0/bin/node",
"args": [
"/Users/huangzz/Documents/Project/N-API/node-addon/index.js"
]
}]
}

从 launch.json 可以看到整个调试的过程为:vscode 插件调用 lldb,启动 nodejs 去执行 index.js,在 js 文件中会调用 node 扩展,而该部分扩展已经包含了调试信息,故而可以用于调试。

文章

https://yunnysunny.gitbooks.io/nodebook/content/10_node_addon.html
https://academy.realm.io/cn/posts/oredev-kenneth-geisshirt-extending-node-js-cpp/

C++扩展源码示例

  1. https://github.com/kneth/DemoNodeExtension

V8 文档

  1. https://v8.dev/

node-addon-api 文档

https://github.com/nodejs/node-addon-api#api-documentation

vscode里混合调试c++和nodejs

由于业务里涉及到 js 和 c++互相调用,为了调试方便,研究下如何在 vscode 里混合调试 c++和 js 代码。

c++开发和调试

在讲混合调试之前,先简单介绍下 vscode 下 c++的开发和调试,后续所有都是在 Mac OS 环境下操作的。

前置条件

安装 c++套件 c++ extension
安装 lldb 套件 vscode-lldb
确认安装 clang

混合调试

因为 c++和 nodejs 走的是不同的调试协议,因此很难通过一个 configuration 来调试两者。 我们以 node 官方的 example https://github.com/nodejs/node-addon-examples/tree/master/1_hello_world/nan, 看看如何进行混合调试

addon 编译

创建 addon 的编译 script

1
2
3
4
5
6
7
8
"scripts": {
"start": "node index.js",
"build:dev": "node-gyp -j 16 build --debug", // 编译带上debug信息
"build": "node-gyp -j 16 build",
"rebuild:dev": "node-gyp -j 16 rebuild --debug",
"rebuild": "node-gyp -j 16 rebuild",
"clean": "node-gyp clean"
},

创建 preLaunchTask

调试程序前,我们需要预先编译好可以 debug 的 addon,因此我们创建一个 preLaunchTask,选择 rebuild:dev,其负责编译一个可以 debug 的 addon 版本
img

创建 launch.json

我们新创建一个启动 node 的 launch,不过我们这里使用 lldb 来启动 node,配置如下

1
2
3
4
5
6
7
8
{
"type": "lldb",
"request": "launch",
"name": "lldb:node",
"program": "/Users/yj/.nvm/versions/node/v12.16.1/bin/node", // 指向node入科
"args": ["examples/addon/index.js"], // 指向js入口
"cwd": "${workspaceFolder}"
},

此时我们启动 lldb:node 就可以进入 node 的 addon 进行 debug

img

这里存在的问题在于,我们这里实际上是使用 lldb 来调试 node 程序(此时的 addon 实际上是一个动态库),lldb 实际上对 js 的调试是无感知的,因此我们这样实际是无法调试 js 的,在 js 里打下的断点也无法进入。

nodejs 调试

实际上 node 的 js 里的调试功能实际上是通过 chrome debugger protocol 协议来实现的,因此我们只需要启动 js 的时候,开启 inspector 协议就然后就可以 attach 到 js 上进行调试。 修改配置,开启 inspector 协议

1
2
3
4
5
6
7
8
{
"type": "lldb",
"request": "launch",
"name": "lldb:node",
"program": "/Users/yj/.nvm/versions/node/v12.16.1/bin/node",
"args": ["--inspect-brk","examples/addon/index.js"], // 开启inspector协议
"cwd": "${workspaceFolder}"
},

此时我们重新启动 lldb:node,发现在终端出来个 websocket 地址
img

在 vscode 里调试 js

实际上当 node 开启 inspector 协议的时候,不仅可以通过 ws attach 到 node 的 js 上,实际上也可以通过 http:port attach 到 js 上。实际上我们的 ws 地址也是从这个 http:port 获取来的 nodejs 在开启 inspect 的时候默认会启动一个 inspect server,其地址为 ​http:127.0.0.1:${inspect_port}​,其inspect_port默认为9229,当然也可以通过node --inspect-brk --port:${inspect_port}来指定其端口。 其暴露了一个接口 ​/json​,通过该接口即可获得其对应的 websocket 地址
img

下面我们来通过 vscode 来调试 js,首先创建一个 launch 任务,此时我们选择 Node.js: attach
img

我们选择通过 port 来 attach 到 js 上,配置如下

1
2
3
4
5
6
7
8
9
{
"type": "node",
"request": "attach",
"name": "node:attach",
"port": 9229, // 绑定到node.js默认的inspect的端口
"skipFiles": [
"<node_internals>/**"
]
},

此时我们只需要启动 lldb:node 启动调试,其会自动的停止在第一行 js 代码上。这样我们就实现了在 vscode 里混合调试 c++和 js

compound 模式

借助于 vscode 的 launch 的 compound 功能,我们甚至可以避免手动的切换 debugger,达到真正的一键调试,配置如下

1
2
3
4
5
6
"compounds": [
{
"name": "node_c++",
"configurations": ["lldb:node","node:attach"]
}
]

这样我们点击 node_c++功能就可以实现一键调试

node 安装 pm2

  1. pm2 安装
    npm install pm2 -g

  2. pm2 的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = {
apps: [
{
name: "lunbuka-m-h5", // 这个name就是pm2启动时需要的name
exec_mode: "cluster",
instances: "max", // Or a number of instances
script: "./node_modules/nuxt/bin/nuxt.js",
args: "start --mode prod",
out_file: "/data/pm2/logs/lunbuka-m-h5_out.log",
error_file: "/data/pm2/logs/lunbuka-m-h5_error.log",
combine_logs: true,
merge_logs: true,
cwd: "/data/web/kalunbu-m-h5", // absolute path to nuxt dir
max_memory_restart: "512M" // in case nuxt renderer eats all memory, it will be restarted
}
]
};

配置文件更改后,需要 kill pm2, 重新启动才能生效

  1. 日志文件分割
    pm2 install pm2-logrotate

    常用命令
    pm2 set pm2-logrotate:max_size 10K
    pm2 set pm2-logrotate:retain 30
    pm2 set pm2-logrotate:compress false
    pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss
    pm2 set pm2-logrotate:workerInterval 30
    pm2 set pm2-logrotate:rotateInterval 0 0 * * *
    pm2 set pm2-logrotate:rotateModule true

  2. pm2 常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# Fork mode
pm2 start app.js --name my-api # Name process

# Cluster mode
pm2 start app.js -i 0 # Will start maximum processes with LB depending on available CPUs
pm2 start app.js -i max # Same as above, but deprecated.
pm2 scale app +3 # Scales `app` up by 3 workers
pm2 scale app 2 # Scales `app` up or down to 2 workers total

# Listing

pm2 list # Display all processes status
pm2 jlist # Print process list in raw JSON
pm2 prettylist # Print process list in beautified JSON

pm2 describe 0 # Display all informations about a specific process

pm2 monit # Monitor all processes

# Logs

pm2 logs [--raw] # Display all processes logs in streaming
pm2 flush # Empty all log files
pm2 reloadLogs # Reload all logs

# Actions

pm2 stop all # Stop all processes
pm2 restart all # Restart all processes

pm2 reload all # Will 0s downtime reload (for NETWORKED apps)

pm2 stop 0 # Stop specific process id
pm2 restart 0 # Restart specific process id

pm2 delete 0 # Will remove process from pm2 list
pm2 delete all # Will remove all processes from pm2 list

# Misc

pm2 reset <process> # Reset meta data (restarted time...)
pm2 updatePM2 # Update in memory pm2
pm2 ping # Ensure pm2 daemon has been launched
pm2 sendSignal SIGUSR2 my-app # Send system signal to script
pm2 start app.js --no-daemon
pm2 start app.js --no-vizion
pm2 start app.js --no-autorestart

参考:https://pm2.keymetrics.io/docs/usage/quick-start/