Skip to content

Commit 73d5987

Browse files
committed
refactor: use process and net to improve restart and lock
1 parent 9e4164d commit 73d5987

File tree

7 files changed

+86
-27
lines changed

7 files changed

+86
-27
lines changed

apps/koa/src/index.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ const run = () => {
1414
const Router = require('koa-router')
1515
const { createFetch } = require('ofetch')
1616

17-
const unregister = register({
18-
autoOpenDevtool: true
19-
})
17+
const unregister = register()
18+
register()
2019

2120
const app = new Koa()
2221
const router = new Router()

packages/network-debugger/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-network-devtools",
3-
"version": "1.0.18",
3+
"version": "1.0.19",
44
"description": "Inspecting Node.js's Network with Chrome DevTools",
55
"homepage": "https://grinzero.github.io/node-network-devtools/",
66
"main": "./dist/index.js",

packages/network-debugger/src/core/fork.ts

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
import { IS_DEV_MODE, READY_MESSAGE, RequestDetail } from '../common'
1+
import { READY_MESSAGE, RequestDetail } from '../common'
22
import { type IncomingMessage } from 'http'
33
import WebSocket from 'ws'
44
import { ChildProcess, fork } from 'child_process'
55
import { __dirname } from '../common'
66
import { resolve as resolvePath } from 'path'
77
import { RegisterOptions } from '../common'
88
import fs from 'fs'
9+
import { sleep, checkMainProcessAlive } from '../utils/process'
10+
import { unlinkSafe } from '../utils/file'
11+
import { warn } from '../utils'
912

10-
let hasLogError = false
13+
class ExpectError extends Error {
14+
constructor(message: string) {
15+
super(message)
16+
}
17+
}
1118

1219
export class MainProcess {
1320
private ws: Promise<WebSocket>
@@ -16,24 +23,34 @@ export class MainProcess {
1623

1724
constructor(props: RegisterOptions & { key: string }) {
1825
this.options = props
19-
this.ws = new Promise<WebSocket>((resolve, reject) => {
26+
this.ws = new Promise<WebSocket>(async (resolve, reject) => {
2027
const lockFilePath = resolvePath(__dirname, `./${props.key}`)
2128
if (fs.existsSync(lockFilePath)) {
22-
fs.watchFile(lockFilePath, (e) => {
23-
if (!fs.existsSync(lockFilePath)) {
24-
reject(new Error('MainProcess is already running'))
25-
}
26-
})
27-
return
29+
// 读取 lock 文件中的进程号
30+
const pid = fs.readFileSync(lockFilePath, 'utf-8')
31+
await sleep(800)
32+
33+
// 检测该进程是否存活且 port 是否被占用
34+
const isProcessAlice = await checkMainProcessAlive(pid, props.port!)
35+
if (isProcessAlice) {
36+
warn(`The main process with same options is already running, skip it.`)
37+
return
38+
}
39+
// 如果进程不存在:
40+
// 1. 热更新导致 process 重启
41+
// 2. 上一个进程未成功删除 lock
42+
// 都应该继续往下
43+
unlinkSafe(lockFilePath)
2844
}
29-
fs.writeFileSync(lockFilePath, `LOCKED`)
45+
fs.writeFileSync(lockFilePath, `${process.pid}`)
3046
const socket = new WebSocket(`ws://localhost:${props.port}`)
3147
socket.on('open', () => {
48+
unlinkSafe(lockFilePath)
3249
resolve(socket)
3350
})
3451
socket.on('error', () => {
3552
this.openProcess(() => {
36-
fs.unlinkSync(lockFilePath)
53+
unlinkSafe(lockFilePath)
3754
const socket = new WebSocket(`ws://localhost:${props.port}`)
3855
socket.on('open', () => {
3956
resolve(socket)
@@ -49,14 +66,14 @@ export class MainProcess {
4966
})
5067
})
5168
.catch((e) => {
52-
if (!hasLogError) {
53-
!IS_DEV_MODE && (hasLogError = true)
54-
console.warn('MainProcess Warning: ', e)
69+
if (e instanceof ExpectError) {
70+
return
5571
}
72+
throw e
5673
})
5774
}
5875

59-
private openProcess(callback?: () => void) {
76+
private openProcess(callback?: (cp: ChildProcess) => void) {
6077
const forkProcess = () => {
6178
// fork a new process with options
6279
const cp = fork(resolvePath(__dirname, './fork'), {
@@ -67,7 +84,7 @@ export class MainProcess {
6784
})
6885
const handleMsg = (e: any) => {
6986
if (e === READY_MESSAGE) {
70-
callback && callback()
87+
callback && callback(cp)
7188
cp.off('message', handleMsg)
7289
}
7390
}
@@ -81,13 +98,10 @@ export class MainProcess {
8198

8299
public async send(data: any) {
83100
const ws = await this.ws.catch((err) => {
84-
if (hasLogError) {
85-
// has error in main process or websocket
101+
if (err instanceof ExpectError) {
86102
return null
87103
}
88-
!IS_DEV_MODE && (hasLogError = true)
89-
console.warn('MainProcess Websocket Error: ', err)
90-
return null
104+
throw err
91105
})
92106
if (!ws) return
93107
ws.send(JSON.stringify(data))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export const log = console.log.bind(console, '\x1B[36m[node-network-debugger]:', '\x1B[32m')
2+
3+
export const warn = console.warn.bind(console, '\x1B[36m[node-network-debugger](warn):', '\x1B[33m')
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import fs from 'fs'
2+
3+
export const unlinkSafe = (path: string) => {
4+
try {
5+
fs.unlinkSync(path)
6+
} catch {}
7+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import net from 'net'
2+
3+
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
4+
5+
export const checkMainProcessAlive = (pid: number | string, port: number) => {
6+
try {
7+
if (Number(pid) === process.pid) {
8+
return Promise.resolve(true)
9+
}
10+
process.kill(Number(pid), 0)
11+
12+
// 检查 port 是否被占用
13+
return new Promise<boolean>((resolve) => {
14+
const server = net.createServer()
15+
16+
server.once('error', (err: any) => {
17+
if (err.code === 'EADDRINUSE') {
18+
// 端口被占用
19+
resolve(false)
20+
}
21+
// fallback 其他错误
22+
resolve(false)
23+
})
24+
25+
server.once('listening', () => {
26+
// 端口未被占用
27+
server.close(() => resolve(true))
28+
})
29+
30+
server.listen(port)
31+
})
32+
} catch {
33+
return Promise.resolve(false)
34+
}
35+
}

packages/network-debugger/vite.config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export default defineConfig(({ mode }) => ({
2626
'node:diagnostics_channel',
2727
'node:async_hooks',
2828
'node:buffer',
29-
'stream'
29+
'stream',
30+
'net'
3031
],
3132
output: {
3233
globals: {
@@ -43,7 +44,8 @@ export default defineConfig(({ mode }) => ({
4344
'node:diagnostics_channel': 'diagnostics_channel',
4445
'node:async_hooks': 'async_hooks',
4546
'node:buffer': 'buffer',
46-
stream: 'stream'
47+
stream: 'stream',
48+
net: 'net'
4749
}
4850
}
4951
}

0 commit comments

Comments
 (0)