From b0209bed76bc0de8919fd81a6db7e294b02663da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=AD=E9=9C=93?= <3120294679@qq.com> Date: Wed, 12 Jun 2024 09:25:13 +0800 Subject: [PATCH] New feature (Auto Restart): index.ts & Logger.ts Added the logic for auto-restart: If the child process does not output logs for an extended period (as defined in config.json as restartTime), it will exit abnormally. If the child process exits abnormally, the main process will attempt to fork a new child process with the same account data for a maximum of five attempts. --- src/config.json | 1 + src/index.ts | 37 ++++++++++++++++++++++++++++--------- src/interface/Config.ts | 1 + src/util/Logger.ts | 17 +++++++++++++++++ 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/config.json b/src/config.json index c5d1509..dedad06 100644 --- a/src/config.json +++ b/src/config.json @@ -12,6 +12,7 @@ "doDesktopSearch": true, "doMobileSearch": true }, + "restartTime": 180000, "globalTimeout": 30000, "searchSettings": { "useGeoLocaleQueries": false, diff --git a/src/index.ts b/src/index.ts index df33302..1efd444 100644 --- a/src/index.ts +++ b/src/index.ts @@ -56,15 +56,12 @@ export class MicrosoftRewardsBot { async run() { log('MAIN', `Bot started with ${this.config.clusters} clusters`) - // Only cluster when there's more than 1 cluster demanded - if (this.config.clusters > 1) { - if (cluster.isPrimary) { - this.runMaster() - } else { - this.runWorker() - } + // No matter what the situation, + // a master will be started to monitor whether the worker needs to be restarted. + if (cluster.isPrimary) { + this.runMaster() } else { - this.runTasks(this.accounts) + this.runWorker() } } @@ -72,11 +69,14 @@ export class MicrosoftRewardsBot { log('MAIN-PRIMARY', 'Primary process started') const accountChunks = this.utils.chunkArray(this.accounts, this.config.clusters) + // Create a Map to store the worker's PID and corresponding account array to facilitate restarting + const workerAccountsMap = new Map() for (let i = 0; i < accountChunks.length; i++) { const worker = cluster.fork() - const chunk = accountChunks[i] + const chunk = accountChunks[i] as Account[] worker.send({ chunk }) + workerAccountsMap.set(worker.process.pid, { accounts: chunk, restartCount: 0 }) } cluster.on('exit', (worker, code) => { @@ -84,14 +84,33 @@ export class MicrosoftRewardsBot { log('MAIN-WORKER', `Worker ${worker.process.pid} destroyed | Code: ${code} | Active workers: ${this.activeWorkers}`, 'warn') + // Restart worker if exit code is not 0 and restart count is less than 5 + if (code !== 0) { + const workerId = worker.process.pid as number + const workerData = workerAccountsMap.get(workerId) + if (workerData) { + if (workerData.restartCount < 5) { + log('QuitUnexpectedly', `Restarting worker ${workerId}... Remaining restarts: ${5 - workerData.restartCount}`, 'warn') + const worker = cluster.fork() + worker.send({ chunk: workerData.accounts }) + this.activeWorkers += 1 + workerAccountsMap.set(worker.process.pid, { accounts: workerData.accounts, restartCount: workerData.restartCount + 1 }) + } else { + log('QuitUnexpectedly', `Worker ${workerId} has reached maximum restart limit.`, 'warn') + } + } + } + // Check if all workers have exited if (this.activeWorkers === 0) { log('MAIN-WORKER', 'All workers destroyed. Exiting main process!', 'warn') process.exit(0) } + workerAccountsMap.delete(worker.process.pid as number) }) } + private runWorker() { log('MAIN-WORKER', `Worker ${process.pid} spawned`) // Receive the chunk of accounts from the master diff --git a/src/interface/Config.ts b/src/interface/Config.ts index 515d96d..26d51f6 100644 --- a/src/interface/Config.ts +++ b/src/interface/Config.ts @@ -5,6 +5,7 @@ export interface Config { runOnZeroPoints: boolean; clusters: number; workers: Workers; + restartTime: number; globalTimeout: number; searchSettings: SearchSettings; webhook: Webhook; diff --git a/src/util/Logger.ts b/src/util/Logger.ts index 39219da..bcf84c9 100644 --- a/src/util/Logger.ts +++ b/src/util/Logger.ts @@ -1,4 +1,8 @@ import { Webhook } from './Webhook' +import cluster from 'cluster' +import {loadConfig} from './Load' + +let timer: NodeJS.Timeout | null = null export function log(title: string, message: string, type?: 'log' | 'warn' | 'error') { const currentTime = new Date().toLocaleString() @@ -23,4 +27,17 @@ export function log(title: string, message: string, type?: 'log' | 'warn' | 'err } if (str) Webhook(str) + + if (cluster.isMaster) { + // 如果是主进程,不设置定时器,直接返回 + return + } + if (timer) { + clearTimeout(timer) + } + timer = setTimeout(() => { + process.exit(1) + // Set a timer. If it is not triggered for a period of time, it will end abnormally. + }, loadConfig().restartTime) + } \ No newline at end of file