|
|
@ -33,7 +33,7 @@ class HeroUnion { |
|
|
|
//默认配置
|
|
|
|
//默认配置
|
|
|
|
//this.task_data_dir = path.resolve('./tmp/data/'); //任务数据保存目录
|
|
|
|
//this.task_data_dir = path.resolve('./tmp/data/'); //任务数据保存目录
|
|
|
|
this.task_cache_time = 86400; //任务数据最长缓存时间,单位:秒
|
|
|
|
this.task_cache_time = 86400; //任务数据最长缓存时间,单位:秒
|
|
|
|
this.task_data_max_size = 1024; //任务数据最大字节数,单位:KB
|
|
|
|
this.task_data_max_size = 1024; //任务数据最大字节数,单位:KB,默认最大1M
|
|
|
|
this.notify_timeout = 8; //回调通知请求超时时长,单位:秒
|
|
|
|
this.notify_timeout = 8; //回调通知请求超时时长,单位:秒
|
|
|
|
this.notify_max_try = 5; //回调通知最多尝试次数
|
|
|
|
this.notify_max_try = 5; //回调通知最多尝试次数
|
|
|
|
this.heroHeartTimeout = 600; //爬虫心跳超时时长,单位:秒
|
|
|
|
this.heroHeartTimeout = 600; //爬虫心跳超时时长,单位:秒
|
|
|
@ -79,7 +79,7 @@ class HeroUnion { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
isDataTooLarge(data) { |
|
|
|
isDataTooLarge(data) { |
|
|
|
return JSON.stringify(data).length > this.task_data_max_size * 1024; |
|
|
|
return common.byteSize(JSON.stringify(data)) > this.task_data_max_size * 1024; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async getConfig(forceReload) { |
|
|
|
async getConfig(forceReload) { |
|
|
@ -247,21 +247,25 @@ class HeroUnion { |
|
|
|
return searchResult; |
|
|
|
return searchResult; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//保存任务处理结果
|
|
|
|
//保存处理中任务结果
|
|
|
|
saveTaskById(uuid, id, data) { |
|
|
|
saveTaskById(bot_name, id, data) { |
|
|
|
let done = false; |
|
|
|
let done = false; |
|
|
|
|
|
|
|
|
|
|
|
let taskIndex = this.tasks.findIndex((item) => item.id == id); |
|
|
|
let taskIndex = this.tasks.findIndex((item) => item.id == id && item.status == 'running'); |
|
|
|
if (taskIndex > -1) { |
|
|
|
if (taskIndex > -1) { |
|
|
|
if (this.isDataTooLarge(data)) { |
|
|
|
if (this.isDataTooLarge(data)) { |
|
|
|
|
|
|
|
//更新统计数据
|
|
|
|
|
|
|
|
this.taskStatus.running --; |
|
|
|
|
|
|
|
this.taskStatus.failed ++; |
|
|
|
|
|
|
|
|
|
|
|
this.tasks[taskIndex].status = 'failed'; |
|
|
|
this.tasks[taskIndex].status = 'failed'; |
|
|
|
this.tasks[taskIndex].error = 'Result is too large to save.'; |
|
|
|
this.tasks[taskIndex].error = 'Result is too large to save.'; |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data.uuid = uuid; |
|
|
|
data.provider = bot_name; //记录数据提供者
|
|
|
|
|
|
|
|
|
|
|
|
let resIndex = this.tasks[taskIndex].results.findeIndex((dataItem) => dataItem.uuid == uuid); |
|
|
|
let resIndex = this.tasks[taskIndex].results.findIndex((dataItem) => dataItem.provider == bot_name); |
|
|
|
if (resIndex == -1) { |
|
|
|
if (resIndex == -1) { |
|
|
|
this.tasks[taskIndex].results.push(data); |
|
|
|
this.tasks[taskIndex].results.push(data); |
|
|
|
}else { |
|
|
|
}else { |
|
|
@ -269,6 +273,11 @@ class HeroUnion { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.tasks[taskIndex].updated = common.getTimestampInSeconds(); |
|
|
|
this.tasks[taskIndex].updated = common.getTimestampInSeconds(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//更新统计数据
|
|
|
|
|
|
|
|
this.taskStatus.running --; |
|
|
|
|
|
|
|
this.taskStatus.done ++; |
|
|
|
|
|
|
|
|
|
|
|
this.tasks[taskIndex].status = 'done'; |
|
|
|
this.tasks[taskIndex].status = 'done'; |
|
|
|
|
|
|
|
|
|
|
|
done = true; |
|
|
|
done = true; |
|
|
@ -297,6 +306,13 @@ class HeroUnion { |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
if (notify_url && /^http(s)?:\/\/[\w\.]+/i.test(notify_url)) { |
|
|
|
if (notify_url && /^http(s)?:\/\/[\w\.]+/i.test(notify_url)) { |
|
|
|
|
|
|
|
//检查任务通知次数是否达到最大尝试次数
|
|
|
|
|
|
|
|
if (task.notify_time > this.notify_max_try) { |
|
|
|
|
|
|
|
common.error('[LIMITED] Task %s notify time has reach the max try number %s', |
|
|
|
|
|
|
|
task.id, this.notify_max_try); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let params = { |
|
|
|
let params = { |
|
|
|
"task_id": task.id, |
|
|
|
"task_id": task.id, |
|
|
|
"task_result": task.results, |
|
|
|
"task_result": task.results, |
|
|
@ -307,14 +323,17 @@ class HeroUnion { |
|
|
|
const response = await axios.post(notify_url, params, {timeout: this.notify_timeout*1000}); |
|
|
|
const response = await axios.post(notify_url, params, {timeout: this.notify_timeout*1000}); |
|
|
|
if (response.status == 200) { |
|
|
|
if (response.status == 200) { |
|
|
|
notified = true; |
|
|
|
notified = true; |
|
|
|
|
|
|
|
|
|
|
|
//TODO: 更新任务notified状态以及notify_time通知次数
|
|
|
|
|
|
|
|
}else { |
|
|
|
}else { |
|
|
|
//TODO: 检查任务通知次数是否达到最大尝试次数
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
common.error('[FAILED] Notify to %s failed, response status: %s, status text: %s, result: %s', |
|
|
|
common.error('[FAILED] Notify to %s failed, response status: %s, status text: %s, result: %s', |
|
|
|
notify_url, response.status, response.statusText, response.data); |
|
|
|
notify_url, response.status, response.statusText, response.data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//更新任务notified状态以及notify_time通知次数
|
|
|
|
|
|
|
|
let taskIndex = this.tasks.findIndex((item) => item.id == task.id); |
|
|
|
|
|
|
|
if (taskIndex) { |
|
|
|
|
|
|
|
this.tasks[taskIndex].notified = notified; |
|
|
|
|
|
|
|
this.tasks[taskIndex].notify_time ++; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}catch(err) { |
|
|
|
}catch(err) { |
|
|
|
common.error('[ERROR] Notify to %s failed: %s', notify_url, err); |
|
|
|
common.error('[ERROR] Notify to %s failed: %s', notify_url, err); |
|
|
@ -410,6 +429,41 @@ class HeroUnion { |
|
|
|
common.log('Cronjob of config auto reload started.'); |
|
|
|
common.log('Cronjob of config auto reload started.'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//定期清理过期的任务
|
|
|
|
|
|
|
|
autoCleanExpiredTasks() { |
|
|
|
|
|
|
|
let _self = this; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const frequence = typeof(this.config.autoCleanTaskFrequence) != 'undefined' |
|
|
|
|
|
|
|
&& this.config.autoCleanTaskFrequence ? this.config.autoCleanTaskFrequence : 600; //10 分钟检查一次
|
|
|
|
|
|
|
|
const cronjob = cron.schedule(`*/${frequence} * * * * *`, () => { |
|
|
|
|
|
|
|
let timestamp = common.getTimestampInSeconds(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let tasksLeft = _self.tasks.reduce(function(accumulator, item) { |
|
|
|
|
|
|
|
if ( |
|
|
|
|
|
|
|
(item.status == 'done' || item.status == 'failed') |
|
|
|
|
|
|
|
&& timestamp - item.created > _self.task_cache_time |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
_self.taskStatus[item.status] --; |
|
|
|
|
|
|
|
_self.taskStatus.total --; |
|
|
|
|
|
|
|
common.log('Task %s is expired, which is created at %s', item.id, item.created); |
|
|
|
|
|
|
|
}else { |
|
|
|
|
|
|
|
accumulator.push(item); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return accumulator; |
|
|
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (tasksLeft) { |
|
|
|
|
|
|
|
_self.tasks = tasksLeft; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, { |
|
|
|
|
|
|
|
scheduled: false |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cronjob.start(); |
|
|
|
|
|
|
|
common.log('Cronjob of auto clean expired tasks started.'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//获取联盟状态
|
|
|
|
//获取联盟状态
|
|
|
|
getStats() { |
|
|
|
getStats() { |
|
|
|
this.stats.taskStatus = this.taskStatus; |
|
|
|
this.stats.taskStatus = this.taskStatus; |
|
|
@ -468,6 +522,7 @@ class HeroUnion { |
|
|
|
await this.getConfig(); |
|
|
|
await this.getConfig(); |
|
|
|
this.autoReloadConfigs(); |
|
|
|
this.autoReloadConfigs(); |
|
|
|
this.heroHeartCheck(); |
|
|
|
this.heroHeartCheck(); |
|
|
|
|
|
|
|
this.autoCleanExpiredTasks(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|