core: fix 100% cpu when sub process fail to start

This commit is contained in:
micooz 2018-11-24 21:20:14 +08:00
parent dd5397aa1e
commit b0939ac4ed
8 changed files with 757 additions and 595 deletions

View File

@ -1,5 +1,17 @@
# 更新日志
## 0.3.1 (2018-11-24)
### :bug: Bug 修复:
- 修复子进程blinksocks启动失败导致 CPU 占满的问题
### 从 0.3.0 更新到 0.3.1
```
$ npm install -g blinksocks-gui@0.3.1 blinksocks@3.x
```
## 0.3.0 (2018-05-14)
### :rocket: 新功能和改进

View File

@ -5,18 +5,12 @@
[![license](https://img.shields.io/npm/l/blinksocks-gui.svg)](https://github.com/blinksocks/blinksocks-gui/blob/master/LICENSE)
[![%e2%9d%a4](https://img.shields.io/badge/made%20with-%e2%9d%a4-ff69b4.svg)](https://github.com/blinksocks/blinksocks-gui)
为 [blinksocks](https://github.com/blinksocks/blinksocks) 封装的 WEB 图形化界面。
> 为 [blinksocks](https://github.com/blinksocks/blinksocks) 封装的 WEB 图形化界面。
![](screenshot-0.png)
![](screenshot-1.png)
![](screenshot-2.png)
## 在线体验(只读模式)
https://gui.blinksocks.org/landing?password=preview
## 特性
- 三大平台支持Windows、Linux、macOS
@ -43,18 +37,10 @@ $ npm install -g blinksocks blinksocks-gui
需要升级时重新执行上面的命令即可。
### 使用预编译版本
> 使用预编译版本无需安装 Node.js 和其他依赖软件,但升级时必须重新下载、解压和替换整个软件包。
下载地址https://github.com/blinksocks/blinksocks-gui/releases
## 启动
### 交互式启动
桌面环境双击直接运行,服务器环境从命令行启动:
```
$ NODE_ENV=production blinksocks-gui
```
@ -86,7 +72,7 @@ info: You can now open blinksocks-gui in browser:
```
### 命令启动
### 一行命令启动
```
$ blinksocks-gui --client --port 3000

1194
core/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "blinksocks-gui",
"version": "0.3.0",
"version": "0.3.1",
"description": "A web based GUI wrapper for blinksocks",
"author": "Micooz",
"files": [
@ -33,6 +33,7 @@
"lodash": "^4.17.10",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"node-ipc-call": "0.0.3",
"pidusage": "^2.0.6",
"prompts": "^0.1.8",
"socket.io": "^2.1.1",
@ -42,7 +43,7 @@
"devDependencies": {
"cross-env": "^5.1.6",
"nodemon": "^1.17.5",
"pkg": "^4.3.1",
"pkg": "^4.3.4",
"rimraf": "^2.6.2"
},
"peerDependencies": {

View File

@ -1,5 +1,6 @@
const pidusage = require('pidusage');
const dateFns = require('date-fns');
const { Callee } = require('node-ipc-call');
const { Hub } = require('blinksocks');
@ -126,9 +127,10 @@ const Monitor = {
};
// process methods mapping
const methods = {
const callee = new Callee();
// process methods mapping
callee.register({
// start hub
'start': async function start(config) {
if (!hub) {
@ -170,22 +172,6 @@ const methods = {
'getConnectionsMetrics': () => Monitor.getConnectionsMetrics(),
// get current process upload traffic and download traffic
'getTrafficMetrics': () => [Monitor.getUploadTrafficMetrics(), Monitor.getDownloadTrafficMetrics()],
};
process.on('message', async (action) => {
if (typeof action !== 'object') {
return;
}
const { type, payload } = action;
const func = methods[type];
if (typeof func !== 'function') {
return;
}
try {
const result = await func(payload);
process.send({ type: type + '/done', payload: result });
} catch (err) {
process.send({ type: type + '/error', payload: err.message });
}
});
callee.listen();

View File

@ -1,9 +1,8 @@
const path = require('path');
const child_process = require('child_process');
const _ = require('lodash');
const { Caller } = require('node-ipc-call');
const db = require('./db');
const logger = require('./logger');
const GeoIP = require('./geoip');
const {
@ -15,92 +14,6 @@ const {
const FORK_SCRIPT = path.resolve(__dirname, '_fork.js');
// create a new sub process
function create() {
const subprocess = child_process.fork(FORK_SCRIPT, {
cwd: RUNTIME_PATH,
silent: process.env.NODE_ENV === 'production',
});
const geoip = new GeoIP();
geoip.put(db.get('runtime.ip').value(), { self: true });
const messageQueue = [];
subprocess.on('message', (message) => {
if (typeof message !== 'object') {
// drop non-object message
return;
}
messageQueue.push(message);
});
subprocess.on('error', (err) => {
logger.error(err.stack);
});
async function send(action) {
return new Promise((resolve, reject) => {
if (!subprocess.connected) {
return reject(Error('child process is not available'));
}
// send message to sub process immediately
subprocess.send(action);
function scanQueue() {
for (let i = 0; i < messageQueue.length; i++) {
const message = messageQueue[i];
// find related message from sub process
if (message.type.indexOf(action.type) === 0) {
const { type, payload } = message;
if (type === action.type + '/error') {
reject(Error(payload));
return i;
}
if (type === action.type + '/done') {
resolve(payload);
return i;
}
}
}
return -1;
}
// consume messageQueue
setImmediate(function consume() {
const index = scanQueue();
// if not found, continue to consume,
if (index < 0) {
setImmediate(consume);
}
// if message found, remove it from queue.
else {
messageQueue.splice(index, 1);
}
});
});
}
return {
// GeoIP instance
geoip: geoip,
// return original <ChildProcess>
get process() {
return subprocess;
},
// call any methods of forked process
async invoke(method, args) {
return send({ type: method, payload: args });
},
// destroy this object
destroy() {
subprocess.kill();
geoip.clear();
},
};
}
module.exports = {
_subprocesses: new Map(/* <id>: <ChildProcess> */),
@ -108,7 +21,12 @@ module.exports = {
async start(id, config) {
let sub = this._subprocesses.get(id);
if (!sub) {
sub = create();
sub = Caller.fork(FORK_SCRIPT, [], {
cwd: RUNTIME_PATH,
silent: process.env.NODE_ENV === 'production',
});
sub.geoip = new GeoIP();
sub.geoip.put(db.get('runtime.ip').value(), { self: true });
this._subprocesses.set(id, sub);
}
try {
@ -127,6 +45,7 @@ module.exports = {
if (sub) {
await sub.invoke('stop');
sub.destroy();
sub.geoip.clear();
this._subprocesses.set(id, null);
} else {
throw Error(`service(${id}) is not found`);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB