process-worker-executor
Version:
将多进程和多线程封装为统一的 Async/Await 调用方式, 简化 IPC 消息通信的复杂性
120 lines (117 loc) • 3.73 kB
JavaScript
const log = require('../utils/logger')
const _ = require('lodash')
class ExecutorClient {
logger
logPath
logFile
#id = 1
#cache = new Map()
disposable = []
constructor(opts) {
this.logPath = opts.logPath
this.logFile = opts.logFile
if (this.logPath && this.logFile) {
const name = this.logFile + '-client.txt'
this.logger = log.create(this.logPath, name)
this.disposable.push(() => {
log.close(name)
})
}
}
#decodeArgs(args) {
return args.map(it => {
if (it.type == 'FUNCTION') {
return (...params) => {
this.#send(it.id, 'ARGS_FUNCTION', params)
}
}
return it
})
}
#encodeReturn(response) {
if (_.isFunction(response)) {
return {
id,
type: 'FUNCTION'
}
}
if (_.isPlainObject(response)) {
return _.mapValues(response, value => {
if (_.isFunction(value)) {
const id = this.#id++
this.#cache.set(id, value.bind(response))
return {
id,
type: 'FUNCTION'
}
}
return value
})
}
return response
}
#createMessage(id, command, data) {
const message = {
id,
command,
data
}
return message
}
async #send(id, command, data) {
const msg = this.#createMessage(id, command, data)
this.onSend(msg)
}
async on(message) {
const { id, command, data, metedata } = message
let result
const args = this.#decodeArgs(data)
try {
switch (command) {
case 'RETURN_FUNCTION':
const [fnId, ...params] = args
const fn = this.#cache.get(fnId)
result = await fn?.(...params)
break
case 'CLOSE':
await this.logger?.info('执行器已经关闭, 资源已经释放...')
for (const dispose of this.disposable) {
await dispose()
}
break
default:
result = await this.onExec(metedata, command, ...args)
break
}
const response = this.#encodeReturn(result)
this.#send(id, 'FINISH', response)
} catch (error) {
this.#send(id, 'ERROR', {
message: error.message,
stack: error.stack
})
}
}
async onExec(metedata, command, ...args) {
this.logger?.info({ command, args, metedata })
const module = require(metedata.scriptPath)
if (_.isFunction(module)) {
const ctx = {
...global,
_metedata: metedata
}
if (command) {
return await module.call(ctx, command, ...args)
}
return await module.call(ctx, ...args)
}
if (_.isPlainObject(module)) {
module._metedata = metedata
return await module[command]?.(...args)
}
throw new Error('脚本导出的不是方法也不是普通对象, 请检查脚本: ' + metedata.scriptPath)
}
async onInit() {}
async onSend() {}
}
module.exports = ExecutorClient