flexbiz-server
Version:
Flexible Server
17 lines (16 loc) • 7.81 kB
JavaScript
const {Worker}=require("worker_threads"),fs=require("fs"),_=require("lodash"),crypto=require("crypto"),moment=require("moment"),redisCache=require("./redis-cache");
class WorkerPool{constructor($filename_i$$,$maxQueue_worker$$=0,$maxWorkers$$=1,$defaultTimeout$$=3E5,$pre_created_workers$$=0,$poolName$$="WorkerPool"){if(!fs.existsSync($filename_i$$))throw Error("Worker file does not exist");this.filename=$filename_i$$;this.maxWorkers=$maxWorkers$$;(this.maxQueue=$maxQueue_worker$$)&&this.maxQueue<$maxWorkers$$&&(this.maxQueue=$maxWorkers$$);this.defaultTimeout=$defaultTimeout$$;this.pre_created_workers=$pre_created_workers$$;this.queue=[];this.queue_running=[];
this.workers={};this.poolName=$poolName$$;Logger.info(this.poolName,"\u0111ang t\u1ea1o s\u1eb5n",$pre_created_workers$$,"workes/",$maxWorkers$$);for($filename_i$$=0;$filename_i$$<Math.min($pre_created_workers$$,$maxWorkers$$);$filename_i$$++)$maxQueue_worker$$=this.createWorker(),this.workers[$maxQueue_worker$$.id]=$maxQueue_worker$$}workerIsAlive($wk$$){return!$wk$$.worker||$wk$$.worker.threadId<=0||moment().diff(moment($wk$$.time_responded),"seconds")>(($wk$$.task||{}).timeout||1E4)/1E3?!1:!0}createWorker(){const $wk$$=
{id:`worker-${crypto.randomBytes(20).toString("hex")}`,isBusy:!1,time_responded:new Date};$wk$$.worker=new Worker(this.filename);$wk$$.worker.on("message",async $message$$=>{$wk$$.time_responded=new Date;if($message$$!=="pong"){var $task$$;try{($task$$=$message$$.id_task?this.queue_running.find($q$$=>$q$$.id==$message$$.id_task):$wk$$.task)&&clearTimeout($task$$.timeoutId);if($task$$&&$task$$.callback)if($message$$.id_task&&$message$$.id_task!==$task$$.id)Logger.error("[",this.poolName,"] K\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 kh\u00f4ng \u0111\u00fang cho task",
$task$$.id,$message$$);else{$task$$.finished=new Date;$task$$.status=5;$task$$.message="\u0110\u00e3 th\u1ef1c hi\u1ec7n xong";$task$$.result=$message$$&&$message$$.map?void 0:$message$$;if($task$$.save_log)try{let {finished:$finished$$,status:$status$$,message:$message$$,result:$result$$}=$task$$;$task$$.save_log&&await redisCache.updateObject($task$$.id,{finished:$finished$$,status:$status$$,message:$message$$,result:$result$$})}catch($e$$){Logger.error("[",this.poolName,"] can't update task status",
$task$$.id,$e$$.message)}this.queue_running=this.queue_running.filter($q$$=>$q$$.id!==$task$$.id);$task$$.callback($message$$,$task$$.id)}delete $wk$$.task;$wk$$.isBusy=!1;this.processQueue()}catch($e$$){Logger.error("[",this.poolName,"]",$e$$);try{delete $wk$$.task,$wk$$.isBusy=!1,this.processQueue()}catch($e$$){Logger.error("[",this.poolName,"] cancel current task",$e$$)}}}});$wk$$.worker.on("error",$error$$=>{Logger.error("Worker error:",$error$$);$wk$$.worker.terminate()});$wk$$.worker.on("exit",
async $code$$=>{Logger.error("[",this.poolName,"]",`: worker exited with code ${$code$$}. Creating new worker...`);if($wk$$.task){let $task$$=this.queue_running.find($q$$=>$q$$.id==$wk$$.task.id);if($task$$){clearTimeout($task$$.timeoutId);$task$$.finished=new Date;$task$$.status=9;$task$$.message="\u0110\u00e3 c\u00f3 l\u1ed7i x\u1ea3y ra trong h\u1ec7 th\u1ed1ng. H\u00e3y th\u1eed l\u1ea1i";let {finished:$finished$$,status:$status$$,message:$message$$,result:$result$$}=$task$$;if($task$$.save_log)try{$task$$.save_log&&
await redisCache.updateObject($task$$.id,{finished:$finished$$,status:$status$$,message:$message$$,result:$result$$})}catch($e$$){Logger.error("[",this.poolName,"] can't update task status",$task$$.id,$e$$.message)}$task$$.callback&&$task$$.callback({error:$message$$},$task$$.id);this.queue_running=this.queue_running.filter($q$$=>$q$$.id!==$task$$.id)}}delete this.workers[$wk$$.id];$code$$=this.createWorker();this.workers[$code$$.id]=$code$$});return $wk$$}async processQueue($id_worker$$){for(let $i$$=
0;$i$$<this.maxWorkers&&this.queue.length!=0;$i$$++){let $wk$$=Object.values(this.workers).find($w$$=>!$w$$.isBusy&&!$w$$.task&&(!$id_worker$$||$id_worker$$==$w$$.id));if(!$wk$$)if(Object.keys(this.workers).length<this.maxWorkers)Logger.info("[",this.poolName,"] t\u1ea1o th\u00eam worker m\u1edbi...",", s\u1ed1 l\u01b0\u1ee3ng workers hi\u1ec7n t\u1ea1i",Object.values(this.workers).length,", max:",this.maxWorkers),$wk$$=this.createWorker(),this.workers[$wk$$.id]=$wk$$;else break;if($wk$$){this.workerIsAlive($wk$$)||
(Logger.error("[",this.poolName,"]: Worker",$wk$$.id,"kh\u00f4ng c\u00f2n ho\u1ea1t \u0111\u1ed9ng. T\u1ea1o l\u1ea1i..."),delete this.workers[$wk$$.id],$wk$$=this.createWorker(),this.workers[$wk$$.id]=$wk$$);const $task$$=this.queue.shift();if($task$$){$task$$.status=2;$task$$.message="\u0110ang th\u1ef1c hi\u1ec7n";this.queue_running.push($task$$);if($task$$.save_log)try{let {finished:$finished$$,status:$status$$,message:$message$$,result:$result$$}=$task$$;$task$$.save_log&&await redisCache.updateObject($task$$.id,
{finished:$finished$$,status:$status$$,message:$message$$,result:$result$$})}catch($e$$){Logger.error("[",this.poolName,"] can't update task status",$task$$.id,$e$$.message)}$wk$$.isBusy=!0;$wk$$.task=$task$$;$task$$.params.id_worker=$wk$$.id;$wk$$.worker.postMessage($task$$.params);$task$$.timeoutId=setTimeout(async()=>{this.queue_running=this.queue_running.filter($q$$=>$q$$.id!==$task$$.id);$task$$.finished=new Date;$task$$.status=9;$task$$.message=`\u0110\u00e3 h\u1ebft th\u1eddi gian th\u1ef1c hi\u1ec7n (Time out: ${$task$$.timeout} ms)`;
let {finished:$finished$$,status:$status$$,message:$message$$,result:$result$$}=$task$$;if($task$$.save_log)try{$task$$.save_log&&await redisCache.updateObject($task$$.id,{finished:$finished$$,status:$status$$,message:$message$$,result:$result$$})}catch($e$$){Logger.error("[",this.poolName,"] can't update task status",$task$$.id,$e$$.message)}$task$$.callback&&$task$$.callback({error:$message$$},$task$$.id);delete $wk$$.task;$wk$$.isBusy=!1;this.processQueue()},$task$$.timeout||this.defaultTimeout)}}}}async pushToQueue($params$$,
$callback$$,$timeout$$=0,$id_worker$$){let $save_log$$=!0;$params$$.id_task||($params$$.id_task=`task-${crypto.randomBytes(20).toString("hex")}`,$save_log$$=!1);$timeout$$=$timeout$$||this.defaultTimeout;const $task$$={id:$params$$.id_task,start:new Date,params:_.cloneDeep($params$$),timeout:$timeout$$,status:0,save_log:$save_log$$};delete $task$$.params.configs;delete $task$$.params.user;delete $task$$.params.configs;delete $task$$.params.data;$task$$.params.req&&(delete $task$$.params.req.user,
delete $task$$.params.req.body,delete ($task$$.params.req.query||{}).access_token);try{try{$save_log$$&&await redisCache.setObject($params$$.id_task,$task$$)}catch($e$$){Logger.error("[",this.poolName,"] Kh\u00f4ng th\u1ec3 l\u01b0u task t\u1edbi redis:",$e$$.message)}this.queue.push({id:$params$$.id_task,params:$params$$,callback:$callback$$,timeout:$timeout$$,status:0,save_log:$save_log$$});this.processQueue($id_worker$$)}catch($e$$){Logger.error("[",this.poolName,"] : l\u1ed7i \u0111\u1ea9y task t\u1edbi queue",
$e$$),$callback$$({error:$e$$.error||$e$$.message||$e$$})}}async exec($args_params$$,$callback$$=()=>{},$timeout$$=0){if(this.maxQueue>0&&this.queue.length>=this.maxQueue)Logger.error("[",this.poolName,"]",": queue is full"),$callback$$({error:"Queue is full"});else if($args_params$$.load)for(let $id_worker$$ of Object.keys(this.workers)){let $params$$=_.cloneDeep($args_params$$);this.pushToQueue($params$$,$callback$$,$timeout$$,$id_worker$$)}else $args_params$$=_.cloneDeep($args_params$$),this.pushToQueue($args_params$$,
$callback$$,$timeout$$)}fullQueue(){return this.maxQueue>0&&this.queue.length>=this.maxQueue}busy(){return Object.values(this.workers).filter($w$$=>!$w$$.isBusy).length==0}destroy(){for(const $worker$$ of Object.values(this.workers))$worker$$.terminate();this.workers={};this.queue=[]}}module.exports=WorkerPool;