@rocketsoftware/eureka-js-client
Version:
A JavaScript implementation the Netflix OSS service registry, Eureka.
183 lines (143 loc) • 6.36 kB
JavaScript
;
exports.__esModule = true;
var _dns = require('dns');
var _dns2 = _interopRequireDefault(_dns);
var _async = require('async');
var _async2 = _interopRequireDefault(_async);
var _shuffle = require('lodash/shuffle');
var _shuffle2 = _interopRequireDefault(_shuffle);
var _xor = require('lodash/xor');
var _xor2 = _interopRequireDefault(_xor);
var _Logger = require('./Logger');
var _Logger2 = _interopRequireDefault(_Logger);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function noop() {}
/*
Locates a Eureka host using DNS lookups. The DNS records are looked up by a naming
convention and TXT records must be created according to the Eureka Wiki here:
https://github.com/Netflix/eureka/wiki/Configuring-Eureka-in-AWS-Cloud
Naming convention: txt.<REGION>.<HOST>
*/
var DnsClusterResolver = function () {
function DnsClusterResolver(config, logger) {
_classCallCheck(this, DnsClusterResolver);
this.logger = logger || new _Logger2.default();
this.serverList = undefined;
this.config = config;
if (!this.config.eureka.ec2Region) {
throw new Error('EC2 region was undefined. ' + 'config.eureka.ec2Region must be set to resolve Eureka using DNS records.');
}
if (this.config.eureka.clusterRefreshInterval) {
this.startClusterRefresh();
}
}
DnsClusterResolver.prototype.resolveEurekaUrl = function resolveEurekaUrl(callback) {
var _this = this;
var retryAttempt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
this.getCurrentCluster(function (err) {
if (err) return callback(err);
if (retryAttempt > 0) {
_this.serverList.push(_this.serverList.shift());
}
var _config$eureka = _this.config.eureka,
port = _config$eureka.port,
servicePath = _config$eureka.servicePath,
ssl = _config$eureka.ssl;
var protocol = ssl ? 'https' : 'http';
callback(null, protocol + '://' + _this.serverList[0] + ':' + port + servicePath);
});
};
DnsClusterResolver.prototype.getCurrentCluster = function getCurrentCluster(callback) {
var _this2 = this;
if (this.serverList) {
return callback(null, this.serverList);
}
this.refreshCurrentCluster(function (err) {
if (err) return callback(err);
return callback(null, _this2.serverList);
});
};
DnsClusterResolver.prototype.startClusterRefresh = function startClusterRefresh() {
var _this3 = this;
var refreshTimer = setInterval(function () {
_this3.refreshCurrentCluster(function (err) {
if (err) _this3.logger.warn(err.message);
});
}, this.config.eureka.clusterRefreshInterval);
refreshTimer.unref();
};
DnsClusterResolver.prototype.refreshCurrentCluster = function refreshCurrentCluster() {
var _this4 = this;
var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : noop;
this.resolveClusterHosts(function (err, hosts) {
if (err) return callback(err);
// if the cluster is the same (aside from order), we want to maintain our order
if ((0, _xor2.default)(_this4.serverList, hosts).length) {
_this4.serverList = hosts;
_this4.logger.info('Eureka cluster located, hosts will be used in the following order', _this4.serverList);
} else {
_this4.logger.debug('Eureka cluster hosts unchanged, maintaining current server list.');
}
callback();
});
};
DnsClusterResolver.prototype.resolveClusterHosts = function resolveClusterHosts() {
var _this5 = this;
var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : noop;
var _config$eureka2 = this.config.eureka,
ec2Region = _config$eureka2.ec2Region,
host = _config$eureka2.host,
preferSameZone = _config$eureka2.preferSameZone;
var dataCenterInfo = this.config.instance.dataCenterInfo;
var metadata = dataCenterInfo ? dataCenterInfo.metadata : undefined;
var availabilityZone = metadata ? metadata['availability-zone'] : undefined;
var dnsHost = 'txt.' + ec2Region + '.' + host;
_dns2.default.resolveTxt(dnsHost, function (err, addresses) {
var _ref;
if (err) {
return callback(new Error('Error resolving eureka cluster for region [' + ec2Region + '] using DNS: [' + err + ']'));
}
var zoneRecords = (_ref = []).concat.apply(_ref, addresses);
var dnsTasks = {};
zoneRecords.forEach(function (zoneRecord) {
dnsTasks[zoneRecord] = function (cb) {
_this5.resolveZoneHosts('txt.' + zoneRecord, cb);
};
});
_async2.default.parallel(dnsTasks, function (error, results) {
if (error) return callback(error);
var hosts = [];
var myZoneHosts = [];
Object.keys(results).forEach(function (zone) {
if (preferSameZone && availabilityZone && zone.lastIndexOf(availabilityZone, 0) === 0) {
myZoneHosts.push.apply(myZoneHosts, results[zone]);
} else {
hosts.push.apply(hosts, results[zone]);
}
});
var combinedHosts = [].concat((0, _shuffle2.default)(myZoneHosts), (0, _shuffle2.default)(hosts));
if (!combinedHosts.length) {
return callback(new Error('Unable to locate any Eureka hosts in any zone via DNS @ ' + dnsHost));
}
callback(null, combinedHosts);
});
});
};
DnsClusterResolver.prototype.resolveZoneHosts = function resolveZoneHosts(zoneRecord, callback) {
var _this6 = this;
_dns2.default.resolveTxt(zoneRecord, function (err, results) {
var _ref2;
if (err) {
_this6.logger.warn('Failed to resolve cluster zone ' + zoneRecord, err.message);
return callback(new Error('Error resolving cluster zone ' + zoneRecord + ': [' + err + ']'));
}
_this6.logger.debug('Found Eureka Servers @ ' + zoneRecord, results);
callback(null, (_ref2 = []).concat.apply(_ref2, results).filter(function (value) {
return !!value;
}));
});
};
return DnsClusterResolver;
}();
exports.default = DnsClusterResolver;