四好公路
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

130 lines
3.4 KiB

'use strict';
const retry = require('retry');
const logger = require('./logging')('kafka-node:ConsumerGroupRecovery');
const assert = require('assert');
const _ = require('lodash');
const GroupCoordinatorNotAvailable = require('./errors/GroupCoordinatorNotAvailableError');
const NotCoordinatorForGroup = require('./errors/NotCoordinatorForGroupError');
const IllegalGeneration = require('./errors/IllegalGenerationError');
const GroupLoadInProgress = require('./errors/GroupLoadInProgressError');
const UnknownMemberId = require('./errors/UnknownMemberIdError');
const RebalanceInProgress = require('./errors/RebalanceInProgressError');
const HeartbeatTimeout = require('./errors/HeartbeatTimeoutError');
const TimeoutError = require('./errors/TimeoutError');
const BrokerNotAvailableError = require('./errors').BrokerNotAvailableError;
const NETWORK_ERROR_CODES = [
'ETIMEDOUT',
'ECONNRESET',
'ESOCKETTIMEDOUT',
'ECONNREFUSED',
'EHOSTUNREACH',
'EADDRNOTAVAIL'
];
const recoverableErrors = [
{
errors: [
GroupCoordinatorNotAvailable,
IllegalGeneration,
GroupLoadInProgress,
RebalanceInProgress,
HeartbeatTimeout,
TimeoutError
]
},
{
errors: [NotCoordinatorForGroup, BrokerNotAvailableError],
handler: function () {
delete this.client.coordinatorId;
}
},
{
errors: [UnknownMemberId],
handler: function () {
this.memberId = null;
}
}
];
function isErrorInstanceOf (error, errors) {
return errors.some(function (errorClass) {
return error instanceof errorClass;
});
}
function ConsumerGroupRecovery (consumerGroup) {
this.consumerGroup = consumerGroup;
this.options = consumerGroup.options;
}
function isNetworkError (error) {
if (error && error.code && error.errno) {
return _.includes(NETWORK_ERROR_CODES, error.code);
}
return false;
}
ConsumerGroupRecovery.prototype.tryToRecoverFrom = function (error, source) {
logger.debug('tryToRecoverFrom', source, error);
this.consumerGroup.ready = false;
this.consumerGroup.stopHeartbeats();
var retryTimeout = false;
var retry =
isNetworkError(error) ||
recoverableErrors.some(function (recoverableItem) {
if (isErrorInstanceOf(error, recoverableItem.errors)) {
recoverableItem.handler && recoverableItem.handler.call(this.consumerGroup, error);
return true;
}
return false;
}, this);
if (retry) {
retryTimeout = this.getRetryTimeout(error);
}
if (retry && retryTimeout) {
logger.debug(
'RECOVERY from %s: %s retrying in %s ms',
source,
this.consumerGroup.client.clientId,
retryTimeout,
error
);
this.consumerGroup.scheduleReconnect(retryTimeout);
} else {
this.consumerGroup.emit('error', error);
}
this.lastError = error;
};
ConsumerGroupRecovery.prototype.clearError = function () {
this.lastError = null;
};
ConsumerGroupRecovery.prototype.getRetryTimeout = function (error) {
assert(error);
if (!this._timeouts) {
this._timeouts = retry.timeouts({
retries: this.options.retries,
factor: this.options.retryFactor,
minTimeout: this.options.retryMinTimeout
});
}
if (this._retryIndex == null || this.lastError == null || error.errorCode !== this.lastError.errorCode) {
this._retryIndex = 0;
}
var index = this._retryIndex++;
if (index >= this._timeouts.length) {
return false;
}
return this._timeouts[index];
};
module.exports = ConsumerGroupRecovery;