blue-fish-redis
Version:
这是一个修复漏洞后的redis
125 lines (124 loc) • 4.67 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisQueueWorker = void 0;
const blue_fish_helper_1 = require("blue-fish-helper");
const coa_echo_1 = require("coa-echo");
const sep = '^^';
class RedisQueueWorker {
constructor(queue) {
this.lock = false;
this.queue = queue;
this.bin = queue.bin;
this.keys = queue.keys;
this.workers = {};
this.doingJob = '';
this.retryAt = 0;
this.io = queue.io;
this.ioRead = queue.io.duplicate();
}
// 初始化任务,interval为上报间隔,默认为10秒上报一次
async init(interval = 10e3) {
if (this.lock)
return;
this.lock = true;
// 队列任务监听器
setInterval(() => {
this.interval().then(blue_fish_helper_1._.noop, blue_fish_helper_1._.noop);
}, interval);
// 持续监听队列
// eslint-disable-next-line no-constant-condition
while (true) {
try {
const key = await this.ioRead.brpoplpush(this.keys.pending, this.keys.doing, 0);
await this.work(key);
}
catch (e) {
coa_echo_1.echo.error('* QueueError:', e);
await blue_fish_helper_1.$.timeout(2000);
}
}
}
// 添加新工作类型
on(name, worker) {
this.workers[name] = worker;
return this.queue.definePusher(name);
}
// 检查队列,force是否强制执行,timeout任务上报超时的时间,默认180秒,interval两次执行间隔,默认60秒
async retry(force = false, timeout = 180e3, interval = 60e3) {
const now = blue_fish_helper_1._.now();
// 如果60秒内执行过且没有强制执行,则忽略
const can = await this.io.set(this.keys.retrying, now, 'PX', interval, 'NX');
if (!can && !force)
return;
const [[, doing = []], [, doing_map = {}]] = await this.io
.pipeline()
.lrange(this.keys.doing, 0, -1)
.hgetall(this.keys.doing_map)
.exec();
const retryJobs = [];
// 遍历map检查是否超时
blue_fish_helper_1._.forEach(doing_map, (time, jobId) => {
doing_map[jobId] = now - blue_fish_helper_1._.toInteger(time);
if (doing_map[jobId] > timeout)
retryJobs.push(jobId);
});
// 遍历doing检查是否超时
blue_fish_helper_1._.forEach(doing, jobId => {
if (doing_map[jobId] === undefined || doing_map[jobId] > timeout)
retryJobs.push(jobId);
});
// 如果存在需要重试的任务
if (retryJobs.length) {
const uniqJobIds = blue_fish_helper_1._.uniq(retryJobs);
await this.io
.pipeline()
.hdel(this.keys.doing_map, ...uniqJobIds)
.lpush(this.keys.pending, ...uniqJobIds)
.exec();
}
}
// 开始工作
async work(job) {
// 准备执行
const now = blue_fish_helper_1._.now();
const can = await this.io.hsetnx(this.keys.doing_map, job, now);
// 如果已经开始,则忽略
if (!can)
return;
// 开始执行
this.doingJob = job;
coa_echo_1.echo.grey('* Queue: start job %s', job);
// 解析Job
const [name, id, data] = job.split(sep);
const worker = this.workers[name];
// 执行Job
if (worker) {
try {
await worker(id, JSON.parse(data));
}
catch (e) {
coa_echo_1.echo.error('* Queue JobError: %s %s', job, e.toString());
}
}
else {
coa_echo_1.echo.error('* Queue JobNotFound: %s %s %s', name, id, data);
}
// 执行结束
this.doingJob = '';
await this.io.pipeline().hdel(this.keys.doing_map, job).lrem(this.keys.doing, 0, job).exec();
coa_echo_1.echo.grey('* Queue: job %s completed in %sms', job, blue_fish_helper_1._.now() - now);
}
// 定时检查
async interval() {
const now = blue_fish_helper_1._.now();
// 如果当前有正在执行的任务,报告最新时间
if (this.doingJob)
await this.io.hset(this.keys.doing_map, this.doingJob, now);
// 每隔60秒重试
if (now - this.retryAt > 60e3) {
this.retry().then(blue_fish_helper_1._.noop, blue_fish_helper_1._.noop);
this.retryAt = now;
}
}
}
exports.RedisQueueWorker = RedisQueueWorker;