redis-smq
Version:
A simple high-performance Redis message queue for Node.js.
137 lines • 5.59 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConsumerHeartbeat = void 0;
const os = require("os");
const redis_smq_common_1 = require("redis-smq-common");
const events_1 = require("../../common/events/events");
const redis_keys_1 = require("../../common/redis-keys/redis-keys");
const events_2 = require("events");
const redis_smq_common_2 = require("redis-smq-common");
const cpuUsageStatsRef = {
cpuUsage: process.cpuUsage(),
time: process.hrtime(),
};
function cpuUsage() {
const currentTimestamp = process.hrtime();
const currentCPUUsage = process.cpuUsage();
const timestampDiff = process.hrtime(cpuUsageStatsRef.time);
const cpuUsageDiff = process.cpuUsage(cpuUsageStatsRef.cpuUsage);
cpuUsageStatsRef.time = currentTimestamp;
cpuUsageStatsRef.cpuUsage = currentCPUUsage;
const hrtime = (time) => {
return time[0] * 1e3 + time[1] / 1e6;
};
const usageTime = (time) => {
return time / 1000;
};
return Object.assign({ percentage: ((usageTime(cpuUsageDiff.user + cpuUsageDiff.system) /
hrtime(timestampDiff)) *
100).toFixed(1) }, cpuUsageDiff);
}
class ConsumerHeartbeat extends events_2.EventEmitter {
constructor(consumer, redisClient) {
super();
this.redisClient = redisClient;
this.consumer = consumer;
const { keyHeartbeats } = consumer.getRedisKeys();
this.keyHeartbeats = keyHeartbeats;
this.keyHeartbeatTimestamps =
redis_keys_1.redisKeys.getMainKeys().keyHeartbeatConsumerWeight;
this.ticker = new redis_smq_common_1.Ticker(() => this.onTick());
this.ticker.nextTick();
}
getPayload() {
const timestamp = Date.now();
return {
timestamp,
data: {
ram: {
usage: process.memoryUsage(),
free: os.freemem(),
total: os.totalmem(),
},
cpu: cpuUsage(),
},
};
}
onTick() {
const timestamp = Date.now();
const heartbeatPayload = this.getPayload();
const heartbeatPayloadStr = JSON.stringify(heartbeatPayload);
const multi = this.redisClient.multi();
multi.hset(this.keyHeartbeats, this.consumer.getId(), heartbeatPayloadStr);
multi.zadd(this.keyHeartbeatTimestamps, timestamp, this.consumer.getId());
multi.exec((err) => {
if (err)
this.emit(events_1.events.ERROR, err);
else {
this.emit(events_1.events.TICK, timestamp, this.consumer.getId(), heartbeatPayload);
this.ticker.nextTick();
}
});
}
quit(cb) {
redis_smq_common_2.async.waterfall([
(cb) => {
this.ticker.once(events_1.events.DOWN, cb);
this.ticker.quit();
},
(cb) => {
const multi = this.redisClient.multi();
ConsumerHeartbeat.handleExpiredHeartbeatId(this.consumer.getId(), multi);
multi.exec((err) => cb(err));
},
(cb) => this.redisClient.halt(cb),
], cb);
}
static isExpiredHeartbeat(heartbeat) {
const { timestamp: heartbeatTimestamp } = heartbeat;
const timestamp = Date.now() - ConsumerHeartbeat.heartbeatTTL;
return heartbeatTimestamp > timestamp;
}
static getConsumersHeartbeats(redisClient, consumerIds, cb) {
const keyHeartbeats = redis_keys_1.redisKeys.getMainKeys().keyHeartbeats;
redisClient.hmget(keyHeartbeats, consumerIds, (err, reply) => {
if (err)
cb(err);
else if (!reply || reply.length !== consumerIds.length)
cb(new redis_smq_common_2.errors.InvalidCallbackReplyError());
else {
const r = {};
redis_smq_common_2.async.eachOf(consumerIds, (item, index, done) => {
const payload = reply[index];
if (payload) {
const consumerHeartbeat = JSON.parse(payload);
r[consumerIds[index]] = this.isExpiredHeartbeat(consumerHeartbeat)
? consumerHeartbeat
: false;
}
else
r[consumerIds[index]] = false;
done();
}, () => cb(null, r));
}
});
}
static getExpiredHeartbeatIds(redisClient, offset, count, cb) {
const { keyHeartbeatConsumerWeight } = redis_keys_1.redisKeys.getMainKeys();
const ts = Date.now() - ConsumerHeartbeat.heartbeatTTL;
redisClient.zrangebyscore(keyHeartbeatConsumerWeight, '-inf', ts, offset, count, (err, consumerIds) => {
if (err)
cb(err);
else
cb(null, consumerIds !== null && consumerIds !== void 0 ? consumerIds : []);
});
}
static handleExpiredHeartbeatId(consumerId, multi) {
const { keyHeartbeats, keyHeartbeatConsumerWeight } = redis_keys_1.redisKeys.getMainKeys();
const ids = typeof consumerId === 'string' ? [consumerId] : consumerId;
ids.forEach((consumerId) => {
multi.hdel(keyHeartbeats, consumerId);
multi.zrem(keyHeartbeatConsumerWeight, consumerId);
});
}
}
exports.ConsumerHeartbeat = ConsumerHeartbeat;
ConsumerHeartbeat.heartbeatTTL = 10 * 1000;
//# sourceMappingURL=consumer-heartbeat.js.map
;