提交 89fc3c02 编写于 作者: S Stephane Nicoll

Add BackOffExecution to isolate state

 This commit separates the BackOff configuration from an actual
 execution. BackOffExecution now contains all the state of a
 particular execution and BackOff is only meant to start (i.e.
 create) a new execution.

 The method "reset" has been removed as its no longer necessary:
 when an execution does not need to be used for a given operation
 anymore it can be simply discarded.

 Issue: SPR-11746
上级 49040a29
......@@ -17,16 +17,19 @@
package org.springframework.util;
/**
* Indicate the rate at which an operation should be retried.
* Provide a {@link BackOffExecution} that indicates the rate at which
* an operation should be retried.
*
* <p>Users of this interface are expected to use it like this:
*
* <pre class="code">
* {@code
*
* long waitInterval = backOff.nextBackOffMillis();
* if (waitInterval == BackOff.STOP) {
* backOff.reset();
* BackOffExecution exec = backOff.start();
*
* // In the operation recovery/retry loop:
* long waitInterval = exec.nextBackOffMillis();
* if (waitInterval == BackOffExecution.STOP) {
* // do not retry operation
* }
* else {
......@@ -35,31 +38,19 @@ package org.springframework.util;
* }
* }</pre>
*
* Once the underlying operation has completed successfully, the instance
* <b>must</b> be {@link #reset()} before further use. Due to how back off
* should be used, implementations do not need to be thread-safe.
* Once the underlying operation has completed successfully, the execution
* instance can be simply discarded.
*
* @author Stephane Nicoll
* @since 4.1
* @see BackOffExecution
*/
public interface BackOff {
/**
* Return value of {@link #nextBackOff()} that indicates that the operation
* should not be retried.
*/
long STOP = -1;
/**
* Return the number of milliseconds to wait before retrying the operation
* or {@link #STOP} ({@value #STOP}) to indicate that no further attempt
* should be made for the operation.
*/
long nextBackOff();
/**
* Reset this instance to its original state.
* Start a new back off execution.
* @return a fresh {@link BackOffExecution} ready to be used
*/
void reset();
BackOffExecution start();
}
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util;
/**
* Represent a particular back-off execution.
*
* <p>Implementations do not need to be thread safe.
*
* @author Stephane Nicoll
* @since 4.1
* @see org.springframework.util.BackOff
*/
public interface BackOffExecution {
/**
* Return value of {@link #nextBackOff()} that indicates that the operation
* should not be retried.
*/
long STOP = -1;
/**
* Return the number of milliseconds to wait before retrying the operation
* or {@link #STOP} ({@value #STOP}) to indicate that no further attempt
* should be made for the operation.
*/
long nextBackOff();
}
......@@ -44,7 +44,8 @@ package org.springframework.util;
*
* Note that the default max elapsed time is {@link Long#MAX_VALUE}. Use
* {@link #setMaxElapsedTime(long)} to limit the maximum number of time
* that an instance should accumulate before returning {@link BackOff#STOP}.
* that an instance should accumulate before returning
* {@link BackOffExecution#STOP}.
*
* @author Stephane Nicoll
* @since 4.1
......@@ -80,9 +81,6 @@ public class ExponentialBackOff implements BackOff {
private long maxElapsedTime = DEFAULT_MAX_ELAPSED_TIME;
private long currentInterval = -1;
private long currentElapsedTime = 0;
/**
* Create an instance with the default settings.
......@@ -112,6 +110,13 @@ public class ExponentialBackOff implements BackOff {
this.initialInterval = initialInterval;
}
/**
* Return the initial interval in milliseconds.
*/
public long getInitialInterval() {
return initialInterval;
}
/**
* The value to multiply the current interval with for each retry attempt.
*/
......@@ -120,6 +125,13 @@ public class ExponentialBackOff implements BackOff {
this.multiplier = multiplier;
}
/**
* Return the value to multiply the current interval with for each retry attempt.
*/
public double getMultiplier() {
return multiplier;
}
/**
* The maximum back off time.
*/
......@@ -127,50 +139,32 @@ public class ExponentialBackOff implements BackOff {
this.maxInterval = maxInterval;
}
/**
* Return the maximum back off time.
*/
public long getMaxInterval() {
return maxInterval;
}
/**
* The maximum elapsed time in milliseconds after which a call to
* {@link #nextBackOff()} returns {@link BackOff#STOP}.
* {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}.
*/
public void setMaxElapsedTime(long maxElapsedTime) {
this.maxElapsedTime = maxElapsedTime;
}
@Override
public long nextBackOff() {
if (currentElapsedTime >= maxElapsedTime) {
return BackOff.STOP;
}
long nextInterval = computeNextInterval();
currentElapsedTime += nextInterval;
return nextInterval;
/**
* Return the maximum elapsed time in milliseconds after which a call to
* {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}.
*/
public long getMaxElapsedTime() {
return maxElapsedTime;
}
@Override
public void reset() {
this.currentInterval = -1;
this.currentElapsedTime = 0;
}
private long computeNextInterval() {
if (this.currentInterval >= this.maxInterval) {
return this.maxInterval;
}
else if (this.currentInterval < 0) {
this.currentInterval = (this.initialInterval < this.maxInterval
? this.initialInterval : this.maxInterval);
}
else {
this.currentInterval = multiplyInterval();
}
return currentInterval;
}
private long multiplyInterval() {
long i = this.currentInterval;
i *= this.multiplier;
return (i > this.maxInterval ? this.maxInterval :i);
public BackOffExecution start() {
return new ExponentialBackOffExecution();
}
private void checkMultiplier(double multiplier) {
......@@ -180,14 +174,56 @@ public class ExponentialBackOff implements BackOff {
}
}
@Override
public String toString() {
String i = (this.currentInterval < 0 ? "n/a" : this.currentInterval + "ms");
final StringBuilder sb = new StringBuilder("ExponentialBackOff{");
sb.append("currentInterval=").append(i);
sb.append(", multiplier=").append(this.multiplier);
sb.append('}');
return sb.toString();
private class ExponentialBackOffExecution implements BackOffExecution {
private long currentInterval = -1;
private long currentElapsedTime = 0;
@Override
public long nextBackOff() {
if (currentElapsedTime >= maxElapsedTime) {
return BackOffExecution.STOP;
}
long nextInterval = computeNextInterval();
currentElapsedTime += nextInterval;
return nextInterval;
}
private long computeNextInterval() {
long maxInterval = getMaxInterval();
if (this.currentInterval >= maxInterval) {
return maxInterval;
}
else if (this.currentInterval < 0) {
long initialInterval = getInitialInterval();
this.currentInterval = (initialInterval < maxInterval
? initialInterval : maxInterval);
}
else {
this.currentInterval = multiplyInterval(maxInterval);
}
return currentInterval;
}
private long multiplyInterval(long maxInterval) {
long i = this.currentInterval;
i *= getMultiplier();
return (i > maxInterval ? maxInterval : i);
}
@Override
public String toString() {
String i = (this.currentInterval < 0 ? "n/a" : this.currentInterval + "ms");
final StringBuilder sb = new StringBuilder("ExponentialBackOff{");
sb.append("currentInterval=").append(i);
sb.append(", multiplier=").append(getMultiplier());
sb.append('}');
return sb.toString();
}
}
}
......@@ -39,7 +39,6 @@ public class FixedBackOff implements BackOff {
private long maxAttempts = UNLIMITED_ATTEMPTS;
private long currentAttempts = 0;
/**
* Create an instance with an interval of {@value #DEFAULT_INTERVAL}
......@@ -87,31 +86,38 @@ public class FixedBackOff implements BackOff {
}
@Override
public long nextBackOff() {
this.currentAttempts++;
if (this.currentAttempts <= this.maxAttempts) {
return this.interval;
}
else {
return BackOff.STOP;
}
public BackOffExecution start() {
return new FixedBackOffExecution();
}
@Override
public void reset() {
this.currentAttempts = 0;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("FixedBackOff{");
sb.append("interval=").append(this.interval);
String attemptValue = (this.maxAttempts == Long.MAX_VALUE ? "unlimited"
: String.valueOf(this.maxAttempts));
sb.append(", currentAttempts=").append(this.currentAttempts);
sb.append(", maxAttempts=").append(attemptValue);
sb.append('}');
return sb.toString();
private class FixedBackOffExecution implements BackOffExecution {
private long currentAttempts = 0;
@Override
public long nextBackOff() {
this.currentAttempts++;
if (this.currentAttempts <= getMaxAttempts()) {
return getInterval();
}
else {
return BackOffExecution.STOP;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("FixedBackOff{");
sb.append("interval=").append(FixedBackOff.this.interval);
String attemptValue = (FixedBackOff.this.maxAttempts == Long.MAX_VALUE ? "unlimited"
: String.valueOf(FixedBackOff.this.maxAttempts));
sb.append(", currentAttempts=").append(this.currentAttempts);
sb.append(", maxAttempts=").append(attemptValue);
sb.append('}');
return sb.toString();
}
}
}
......@@ -34,64 +34,73 @@ public class ExponentialBackOffTests {
@Test
public void defaultInstance() {
ExponentialBackOff backOff = new ExponentialBackOff();
assertEquals(2000l, backOff.nextBackOff());
assertEquals(3000l, backOff.nextBackOff());
assertEquals(4500l, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(2000l, execution.nextBackOff());
assertEquals(3000l, execution.nextBackOff());
assertEquals(4500l, execution.nextBackOff());
}
@Test
public void simpleIncrease() {
ExponentialBackOff backOff = new ExponentialBackOff(100L, 2.0);
assertEquals(100l, backOff.nextBackOff());
assertEquals(200l, backOff.nextBackOff());
assertEquals(400l, backOff.nextBackOff());
assertEquals(800l, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(100l, execution.nextBackOff());
assertEquals(200l, execution.nextBackOff());
assertEquals(400l, execution.nextBackOff());
assertEquals(800l, execution.nextBackOff());
}
@Test
public void fixedIncrease() {
ExponentialBackOff backOff = new ExponentialBackOff(100L, 1.0);
backOff.setMaxElapsedTime(300l);
assertEquals(100l, backOff.nextBackOff());
assertEquals(100l, backOff.nextBackOff());
assertEquals(100l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(100l, execution.nextBackOff());
assertEquals(100l, execution.nextBackOff());
assertEquals(100l, execution.nextBackOff());
assertEquals(BackOffExecution.STOP, execution.nextBackOff());
}
@Test
public void maxIntervalReached() {
ExponentialBackOff backOff = new ExponentialBackOff(2000L, 2.0);
backOff.setMaxInterval(4000L);
assertEquals(2000l, backOff.nextBackOff());
assertEquals(4000l, backOff.nextBackOff());
assertEquals(4000l, backOff.nextBackOff()); // max reached
assertEquals(4000l, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(2000l, execution.nextBackOff());
assertEquals(4000l, execution.nextBackOff());
assertEquals(4000l, execution.nextBackOff()); // max reached
assertEquals(4000l, execution.nextBackOff());
}
@Test
public void maxAttemptsReached() {
ExponentialBackOff backOff = new ExponentialBackOff(2000L, 2.0);
backOff.setMaxElapsedTime(4000L);
assertEquals(2000l, backOff.nextBackOff());
assertEquals(4000l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff()); // > 4 sec wait in total
BackOffExecution execution = backOff.start();
assertEquals(2000l, execution.nextBackOff());
assertEquals(4000l, execution.nextBackOff());
assertEquals(BackOffExecution.STOP, execution.nextBackOff()); // > 4 sec wait in total
}
@Test
public void resetInstance() {
public void startReturnDifferentInstances() {
ExponentialBackOff backOff = new ExponentialBackOff();
backOff.setInitialInterval(2000L);
backOff.setMultiplier(2.0);
backOff.setMaxElapsedTime(4000L);
assertEquals(2000l, backOff.nextBackOff());
assertEquals(4000l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff());
backOff.reset();
BackOffExecution execution = backOff.start();
BackOffExecution execution2 = backOff.start();
assertEquals(2000l, backOff.nextBackOff());
assertEquals(4000l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff());
assertEquals(2000l, execution.nextBackOff());
assertEquals(2000l, execution2.nextBackOff());
assertEquals(4000l, execution.nextBackOff());
assertEquals(4000l, execution2.nextBackOff());
assertEquals(BackOffExecution.STOP, execution.nextBackOff());
assertEquals(BackOffExecution.STOP, execution2.nextBackOff());
}
@Test
......@@ -107,18 +116,20 @@ public class ExponentialBackOffTests {
ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0);
backOff.setMaxInterval(50L);
assertEquals(50L, backOff.nextBackOff());
assertEquals(50L, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(50L, execution.nextBackOff());
assertEquals(50L, execution.nextBackOff());
}
@Test
public void toStringContent() {
ExponentialBackOff backOff = new ExponentialBackOff(2000L, 2.0);
assertEquals("ExponentialBackOff{currentInterval=n/a, multiplier=2.0}", backOff.toString());
backOff.nextBackOff();
assertEquals("ExponentialBackOff{currentInterval=2000ms, multiplier=2.0}", backOff.toString());
backOff.nextBackOff();
assertEquals("ExponentialBackOff{currentInterval=4000ms, multiplier=2.0}", backOff.toString());
BackOffExecution execution = backOff.start();
assertEquals("ExponentialBackOff{currentInterval=n/a, multiplier=2.0}", execution.toString());
execution.nextBackOff();
assertEquals("ExponentialBackOff{currentInterval=2000ms, multiplier=2.0}", execution.toString());
execution.nextBackOff();
assertEquals("ExponentialBackOff{currentInterval=4000ms, multiplier=2.0}", execution.toString());
}
}
......@@ -28,57 +28,62 @@ public class FixedBackOffTests {
@Test
public void defaultInstance() {
FixedBackOff backOff = new FixedBackOff();
BackOffExecution execution = backOff.start();
for (int i = 0; i < 100; i++) {
assertEquals(FixedBackOff.DEFAULT_INTERVAL, backOff.nextBackOff());
assertEquals(FixedBackOff.DEFAULT_INTERVAL, execution.nextBackOff());
}
}
@Test
public void noAttemptAtAll() {
FixedBackOff backOff = new FixedBackOff(100L, 0L);
assertEquals(BackOff.STOP, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(BackOffExecution.STOP, execution.nextBackOff());
}
@Test
public void maxAttemptsReached() {
FixedBackOff backOff = new FixedBackOff(200L, 2);
assertEquals(200l, backOff.nextBackOff());
assertEquals(200l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(200l, execution.nextBackOff());
assertEquals(200l, execution.nextBackOff());
assertEquals(BackOffExecution.STOP, execution.nextBackOff());
}
@Test
public void resetOnInstance() {
public void startReturnDifferentInstances() {
FixedBackOff backOff = new FixedBackOff(100L, 1);
assertEquals(100l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
BackOffExecution execution2 = backOff.start();
backOff.reset();
assertEquals(100l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff());
assertEquals(100l, execution.nextBackOff());
assertEquals(100l, execution2.nextBackOff());
assertEquals(BackOffExecution.STOP, execution.nextBackOff());
assertEquals(BackOffExecution.STOP, execution2.nextBackOff());
}
@Test
public void liveUpdate() {
FixedBackOff backOff = new FixedBackOff(100L, 1);
assertEquals(100l, backOff.nextBackOff());
BackOffExecution execution = backOff.start();
assertEquals(100l, execution.nextBackOff());
backOff.setInterval(200l);
backOff.setMaxAttempts(2);
assertEquals(200l, backOff.nextBackOff());
assertEquals(BackOff.STOP, backOff.nextBackOff());
assertEquals(200l, execution.nextBackOff());
assertEquals(BackOffExecution.STOP, execution.nextBackOff());
}
@Test
public void toStringContent() {
FixedBackOff backOff = new FixedBackOff(200L, 10);
assertEquals("FixedBackOff{interval=200, currentAttempts=0, maxAttempts=10}", backOff.toString());
backOff.nextBackOff();
assertEquals("FixedBackOff{interval=200, currentAttempts=1, maxAttempts=10}", backOff.toString());
backOff.nextBackOff();
assertEquals("FixedBackOff{interval=200, currentAttempts=2, maxAttempts=10}", backOff.toString());
BackOffExecution execution = backOff.start();
assertEquals("FixedBackOff{interval=200, currentAttempts=0, maxAttempts=10}", execution.toString());
execution.nextBackOff();
assertEquals("FixedBackOff{interval=200, currentAttempts=1, maxAttempts=10}", execution.toString());
execution.nextBackOff();
assertEquals("FixedBackOff{interval=200, currentAttempts=2, maxAttempts=10}", execution.toString());
}
}
......@@ -35,6 +35,7 @@ import org.springframework.scheduling.SchedulingAwareRunnable;
import org.springframework.scheduling.SchedulingTaskExecutor;
import org.springframework.util.Assert;
import org.springframework.util.BackOff;
import org.springframework.util.BackOffExecution;
import org.springframework.util.ClassUtils;
import org.springframework.util.FixedBackOff;
......@@ -221,8 +222,8 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/**
* Specify the {@link BackOff} instance to use to compute the interval
* between recovery attempts. If the {@link BackOff} implementation
* returns {@link BackOff#STOP}, this listener container will not further
* between recovery attempts. If the {@link BackOffExecution} implementation
* returns {@link BackOffExecution#STOP}, this listener container will not further
* attempt to recover.
* <p>The {@link #setRecoveryInterval(long) recovery interval} is ignored
* when this property is set.
......@@ -897,6 +898,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #stop()
*/
protected void refreshConnectionUntilSuccessful() {
BackOffExecution execution = backOff.start();
while (isRunning()) {
try {
if (sharedConnectionEnabled()) {
......@@ -907,7 +909,6 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
JmsUtils.closeConnection(con);
}
logger.info("Successfully refreshed JMS Connection");
backOff.reset();
break;
}
catch (Exception ex) {
......@@ -917,7 +918,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
StringBuilder msg = new StringBuilder();
msg.append("Could not refresh JMS Connection for destination '");
msg.append(getDestinationDescription()).append("' - retrying using ");
msg.append(this.backOff).append(". Cause: ");
msg.append(execution).append(". Cause: ");
msg.append(ex instanceof JMSException ? JmsUtils.buildExceptionMessage((JMSException) ex) : ex.getMessage());
if (logger.isDebugEnabled()) {
logger.error(msg, ex);
......@@ -926,7 +927,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
logger.error(msg);
}
}
if (!applyBackOffTime()) {
if (!applyBackOffTime(execution)) {
stop();
}
}
......@@ -952,13 +953,14 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
}
/**
* Apply the next back off time. Return {@code true} if the back off period has
* been applied and a new attempt to recover should be made, {@code false} if no
* further attempt should be made.
* Apply the next back off time using the specified {@link BackOffExecution}.
* <p>Return {@code true} if the back off period has been applied and a new
* attempt to recover should be made, {@code false} if no further attempt
* should be made.
*/
protected boolean applyBackOffTime() {
long interval = backOff.nextBackOff();
if (interval == BackOff.STOP) {
protected boolean applyBackOffTime(BackOffExecution execution) {
long interval = execution.nextBackOff();
if (interval == BackOffExecution.STOP) {
return false;
}
else {
......
......@@ -322,7 +322,7 @@
<xsd:annotation>
<xsd:documentation><![CDATA[
Specify the BackOff instance to use to compute the interval between recovery
attempts. If the BackOff implementation returns "BackOff#STOP", the listener
attempts. If the BackOff implementation returns "BackOffExecution#STOP", the listener
container will not further attempt to recover. The recovery-interval value is
ignored when this property is set. The default is a FixedBackOff with an
interval of 5000 ms, that is 5 seconds.
......
......@@ -29,6 +29,7 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.util.BackOff;
import org.springframework.util.BackOffExecution;
/**
*
......@@ -39,7 +40,9 @@ public class DefaultMessageListenerContainerTests {
@Test
public void applyBackOff() {
BackOff mock = mock(BackOff.class);
given(mock.nextBackOff()).willReturn(BackOff.STOP);
BackOffExecution execution = mock(BackOffExecution.class);
given(execution.nextBackOff()).willReturn(BackOffExecution.STOP);
given(mock.start()).willReturn(execution);
DefaultMessageListenerContainer container = createContainer(mock, createFailingContainerFactory());
container.start();
......@@ -48,34 +51,40 @@ public class DefaultMessageListenerContainerTests {
container.refreshConnectionUntilSuccessful();
assertEquals(false, container.isRunning());
verify(mock).nextBackOff();
verify(mock).start();
verify(execution).nextBackOff();
}
@Test
public void applyBackOffRetry() {
BackOff mock = mock(BackOff.class);
given(mock.nextBackOff()).willReturn(50L, BackOff.STOP);
BackOffExecution execution = mock(BackOffExecution.class);
given(execution.nextBackOff()).willReturn(50L, BackOffExecution.STOP);
given(mock.start()).willReturn(execution);
DefaultMessageListenerContainer container = createContainer(mock, createFailingContainerFactory());
container.start();
container.refreshConnectionUntilSuccessful();
assertEquals(false, container.isRunning());
verify(mock, times(2)).nextBackOff();
verify(mock).start();
verify(execution, times(2)).nextBackOff();
}
@Test
public void recoverResetBackOff() {
BackOff mock = mock(BackOff.class);
given(mock.nextBackOff()).willReturn(50L, 50L, 50L); // 3 attempts max
BackOffExecution execution = mock(BackOffExecution.class);
given(execution.nextBackOff()).willReturn(50L, 50L, 50L); // 3 attempts max
given(mock.start()).willReturn(execution);
DefaultMessageListenerContainer container = createContainer(mock, createRecoverableContainerFactory(1));
container.start();
container.refreshConnectionUntilSuccessful();
assertEquals(true, container.isRunning());
verify(mock, times(1)).nextBackOff(); // only on attempt as the second one lead to a recovery
verify(mock, times(1)).reset(); // reset should have been called
verify(mock).start();
verify(execution, times(1)).nextBackOff(); // only on attempt as the second one lead to a recovery
}
@SuppressWarnings("unchecked")
......
......@@ -41433,10 +41433,10 @@ choices and message redelivery scenarios.
| back-off
| Specify the `BackOff` instance to use to compute the interval between recovery
attempts. If the `BackOff` implementation returns `BackOff#STOP`, the listener
container will not further attempt to recover. The `recovery-interval value is
is ignored when this property is set. The default is a `FixedBackOff` with an
interval of 5000 ms, that is 5 seconds.
attempts. If the `BackOffExecution` implementation returns `BackOffExecution#STOP`,
the listener container will not further attempt to recover. The `recovery-interval
value is ignored when this property is set. The default is a `FixedBackOff` with
an interval of 5000 ms, that is 5 seconds.
| recovery-interval
| Specify the interval between recovery attempts, in milliseconds. Convenience
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册