@dazejs/framework
Version:
Daze.js - A powerful web framework for Node.js
146 lines (134 loc) • 4.35 kB
text/typescript
/**
* Copyright (c) 2018 Chan Zewail
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
import cluster from 'cluster';
import debuger from 'debug';
import * as http from 'http';
import * as net from 'net';
import { Deferred } from '../utils/defered';
import { RELOAD_SIGNAL, STIKCY_CONNECTION, WORKER_DID_FORKED, WORKER_DISCONNECT, WORKER_DYING } from './const';
const debug = debuger('daze-framework:cluster');
export interface WorkerOptions {
port: number;
sticky: boolean;
createServer: (...args: any[]) => http.Server;
}
const defaultOptions = {
port: 0,
sticky: false,
createServer: () => {
//
},
};
/**
* @class
*/
export class Worker {
/**
* worker options
*/
options: WorkerOptions;
/**
* worker server
*/
server: net.Server;
constructor(opts: WorkerOptions) {
this.options = Object.assign({}, defaultOptions, opts);
}
// disconnect worker
disconnect(refork = true) {
const { worker } = cluster;
if (!worker) return;
if (Reflect.getMetadata(WORKER_DYING, worker)) return;
Reflect.defineMetadata(WORKER_DYING, true, worker);
debug('worker disconnect');
if (refork) {
// You need to re-fork the new work process
// 需要重新 fork 新的工作进程
// Send notification to the main process: the work process is closing
// 给主进程发送通知:工作进程即将关闭
worker.send(WORKER_DISCONNECT);
// The daze-did-fork, which is sent back by the main process
// indicates that a new work process has been forked
// and that the current process can be closed
// 然后接收主进程发送回来的 daze-did-fork 表示已 fork 了一个新的工作进程,当前进程可以关闭了
worker.once('message', (message: string) => {
if (message === WORKER_DID_FORKED) {
this.close();
}
});
} else {
// Instead of having to fork the new work process, close the service directly
// 不需要重新 fork 新的工作进程,直接关闭服务
this.close();
}
}
close() {
// Close the process timeout
// 关闭进程超时时间
let timer: NodeJS.Timeout;
const killTimeout = 10 * 1000;
if (killTimeout > 0) {
timer = setTimeout(() => {
debug(`process exit by killed(timeout: ${killTimeout}ms), pid: ${process.pid}`);
// eslint-disable-next-line no-process-exit
process.exit(1);
}, killTimeout);
}
const { worker } = cluster;
if (!worker) return;
debug((`start close server, pid: ${process.pid}`));
this.server.close(() => {
debug(`server closed, pid: ${process.pid}`);
try {
worker.disconnect();
clearTimeout(timer);
} catch (e) {
debug(`already disconnect, pid:${process.pid}`);
}
});
}
catcheReloadSignal() {
process.on('message', (message) => {
if (message !== RELOAD_SIGNAL) return;
this.disconnect(true);
});
}
/**
* Start the service
* 启动服务
*/
run(): Promise<http.Server> {
const deferred = new Deferred<any>();
this.catcheReloadSignal();
if (!this.options.sticky) {
this.server = this.options.createServer(this.options.port, () => {
debug(`server resolved on: ${this.options.port}`);
deferred.resolve(this.server);
});
this.server.on('connection', () => {
debug(`Request handled by worker: ${process.pid}`);
});
} else {
process.on('message', (message, connection: any) => {
if (message !== STIKCY_CONNECTION) return;
debug('WORKER #%d got conn from %s', process.pid, connection.remoteAddress);
// emulate a connection event on the server by emitting the
// event with the connection master sent to us
// 通过使用发送给我们的连接主发送事件,模拟服务器上的连接事件
this.server.emit('connection', connection);
// resume as we already caught the conn
// 从一个暂停的套接字开始读数据
connection.resume();
});
this.server = this.options.createServer(0, 'localhost', () => {
debug(`server resolved on: ${0}`);
deferred.resolve(this.server);
});
}
return deferred.promise;
}
}