diff --git a/src/EurekaClient.js b/src/EurekaClient.js index 46ff03f..4b2b2b2 100644 --- a/src/EurekaClient.js +++ b/src/EurekaClient.js @@ -606,7 +606,7 @@ export default class Eureka extends EventEmitter { // Perform retry if request failed and we have attempts left const responseInvalid = response && response.statusCode - && String(response.statusCode)[0] === '5'; + && this.shouldRetry(response.statusCode) if ((error || responseInvalid) && retryAttempt < this.config.eureka.maxRetries) { const nextRetryDelay = this.config.eureka.requestRetryDelay * (retryAttempt + 1); @@ -622,4 +622,14 @@ export default class Eureka extends EventEmitter { }); } + shouldRetry(statusCode) { + //retryableStatusCodes is a comma separated list of ranges (ie 401-403,500-502) + //so go through each range and check if the status code falls inside of any of them + const retryableRanges = this.config.eureka.retryableStatusCodes.split(",") + return retryableRanges + .some(range => { + const [lower, upper] = range.split("-") + return (statusCode >= parseInt(lower) && statusCode <= parseInt(upper)) + }) + } } diff --git a/src/defaultConfig.js b/src/defaultConfig.js index 3c97d91..6becd5c 100644 --- a/src/defaultConfig.js +++ b/src/defaultConfig.js @@ -7,6 +7,7 @@ export default { registryFetchInterval: 30000, maxRetries: 3, requestRetryDelay: 500, + retryableStatusCodes: "500-599", fetchRegistry: true, filterUpInstances: true, servicePath: '/eureka/v2/apps/', diff --git a/test/EurekaClient.test.js b/test/EurekaClient.test.js index 99a943b..4268fb7 100644 --- a/test/EurekaClient.test.js +++ b/test/EurekaClient.test.js @@ -1099,7 +1099,7 @@ describe('Eureka client', () => { const overrides = { eureka: { serviceUrls: { - default: ['http://serverA', 'http://serverB'], + default: ['http://serverA', 'http://serverB', 'http://serverC'], }, maxRetries: 3, requestRetryDelay: 0, @@ -1109,7 +1109,8 @@ describe('Eureka client', () => { const client = new Eureka(config); const requestStub = sinon.stub(request, 'get'); requestStub.onCall(0).yields(null, { statusCode: 500 }, null); - requestStub.onCall(1).yields(null, { statusCode: 200 }, null); + requestStub.onCall(1).yields(null, { statusCode: 400 }, null); + requestStub.onCall(2).yields(null, { statusCode: 200 }, null); client.eurekaRequest({ uri: '/path' }, (error) => { expect(error).to.be.null; expect(requestStub).to.be.calledTwice; @@ -1118,6 +1119,32 @@ describe('Eureka client', () => { done(); }); }); + it('should retry next server on configured request failure', (done) => { + const overrides = { + eureka: { + serviceUrls: { + default: ['http://serverA', 'http://serverB', 'http://serverC'], + }, + maxRetries: 3, + requestRetryDelay: 0, + retryableStatusCodes: "400-404,500-504" + }, + }; + const config = makeConfig(overrides); + const client = new Eureka(config); + const requestStub = sinon.stub(request, 'get'); + requestStub.onCall(0).yields(null, { statusCode: 500 }, null); + requestStub.onCall(1).yields(null, { statusCode: 400 }, null); + requestStub.onCall(2).yields(null, { statusCode: 200 }, null); + client.eurekaRequest({ uri: '/path' }, (error) => { + expect(error).to.be.null; + expect(requestStub).to.be.calledThrice; + expect(requestStub.args[0][0]).to.have.property('baseUrl', 'http://serverA'); + expect(requestStub.args[1][0]).to.have.property('baseUrl', 'http://serverB'); + expect(requestStub.args[2][0]).to.have.property('baseUrl', 'http://serverC'); + done(); + }); + }); });