Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.
About this article
Today we retry to understand this thing, we should contact a lot in the process of daily work, such as docking a third party, it is difficult to ensure that each call can be normal response, as we all know external services are generally not reliable for the caller, especially under the condition of the network environment more bad, actually this is not just multilateral interaction, this system also is very normal, For example, service A calls service B, and service B suddenly drops. This is normal. However, we should take into account that a series of unknown problems may occur behind this normal situation. For example, I went to the third party to notify the purchase of a member, but the notification failed after the user paid the money. So this kind of serious and predictable problem, we should avoid in time, colloquially speaking, there is a word called “compensation mechanism”.
I think retry, mainly consider the following two scenarios:
First: is the occurrence of error, such as null pointer, request timeout, HTTP status exception and so on
Second: business errors, such as failure to modify a state, setting a certain data does not reach the desired value.
Frame natural support
As we mentioned above, retry scenarios are common, so there must be some framework that does this for us right out of the box.
-
Spring’s retry
-
Guava-Retry
Today we will focus on the second Guava retry mechanism because it is much more flexible in certain scenarios than the first. We’ll write a comparison later on why it’s flexible.
Guava Retrying is a flexible and convenient retry component that includes multiple retry strategies and is easy to extend. Let’s see how the author describes it:
This is a small extension to Google’s Guava Library, which allows you to work with different retrying strategies arbitrary function call, such as something that talks to a remote service with flaky uptime.
With Guava-Retrying you can customize the retries and also monitor the results and behavior of each retry. The most important retries based on Guava-style retries are really convenient.
The code shown
1. Pom files are imported
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
Copy the code
2. Define Callable to make retry calls
/ * * *@descSync members * We use Boolean variables to determine *@author yn
* @dateBetter 2021/10/09 * /
private static Callable<Boolean> syncMember = new Callable<Boolean>() {
@Override
public Boolean call(a) throws Exception {
// Business implementation, call third party member interface
String url = ConfigureUtil.get(Memberstants.MEMBER_URL);
String result = HttpMethod.post(url, new ArrayList<BasicNameValuePair>());
if(StringUtils.isEmpty(result)){
throw new RemoteException("Failed to synchronize member. Please try again.");
}
List<Member> member = JSON.parseArray(result, Member.class);
if(CollectionUtils.isNotEmpty(member)){
// The synchronization succeeded
return true;
}
// Synchronization failed
return false; }};Copy the code
I’m going to say one more thing about why it’s called callable, but let’s look at Retryer at the bottom. Because of this call, the method to retry must be of type Callable.
3. Define Retry objects and set related policies
Retryer<Boolean> retryer = RetryerBuilder
.<Boolean>newBuilder()
// Runtime and Checked exceptions will be retried, but error will not be retried.
.retryIfException()
// Return false and retry
.retryIfResult(Predicates.equalTo(false))
// Reset the policy
.withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS))
// Number of attempts
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
// Monitor mechanism
.withRetryListener(new MyRetryListener<>())
.build();
try {
retryer.call(updateReimAgentsCall);
} catch (ExecutionException e) {
logger.error("Failed to synchronize members, need to send a reminder email");
} catch (RetryException e) {
logger.error("Failed to synchronize members, need to send a reminder email");
}
Copy the code
OK, three simple steps to the retry mechanism, and you can use it in your field.
Let’s see what the parameters of each step mean.
-
RetryerBuilder is a factory creator. You can customize the retry source and support multiple retry sources. You can configure the retry times or retry timeout, and you can configure the waiting interval to create Retryer instances.
-
The retry source of RetryerBuilder supports Exception and custom assertion objects, which are set by retryIfException and retryIfResult. Multiple and compatible objects are supported simultaneously.
-
RetryIfException: Runtime and Checked exceptions will be retried, but error will not be retried.
-
RetryIfRuntimeException retries only when runtime exceptions are thrown. Checked and Error are not retried.
-
RetryIfExceptionOfType allows us to retry only when certain exceptions occur, such as NullPointerException and IllegalStateException, which are Runtime exceptions.
Error can also be customized:
.retryIfExceptionOfType(Error.class)// Retry only when error is thrown
Copy the code
We can also specify the Callable method to retry at a specific return value, such as
// Return false and retry
.retryIfResult(Predicates.equalTo(false))
// Retry with _error
.retryIfResult(Predicates.containsPattern("_error$"))
Copy the code
There is also a problem. If we need to do some extra processing after a retry, such as sending an alarm email, we can use RetryListener. After each retry, Guava-Retrying automatically calls back the listeners we registered. Multiple RetryListeners can be registered and are called in the order in which they were registered. Beautiful. These questions are considered in every aspect:
public class MyRetryListener<Boolean> implements RetryListener {
@Override
public <Boolean> void onRetry(Attempt<Boolean> attempt) {
// The number of retries,(note: the first retry is actually the first call)
System.out.print("[retry]time=" + attempt.getAttemptNumber());
// The delay from the first retry
System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt());
// Retry result: Abnormal termination or normal return
System.out.print(",hasException=" + attempt.hasException());
System.out.print(",hasResult=" + attempt.hasResult());
// What causes the exception
if (attempt.hasException()) {
System.out.print(",causeBy=" + attempt.getExceptionCause().toString());
} else {
// The result of a normal return
System.out.print(",result=" + attempt.getResult());
}
// Bad practice: Added extra exception handling code
try {
Boolean result = attempt.get();
System.out.print(",rude get=" + result);
} catch (ExecutionException e) {
System.err.println("this attempt produce exception."+ e.getCause().toString()); } System.out.println(); }}Copy the code
The listener event is set in this method:
/ / loading
.withRetryListener(new MyRetryListener<>())
Copy the code
OK, that’s all for today’s retry mechanism.
overtones
Thank you for reading, if you feel that you have learned something, please like, follow. Also welcome to have a question we comment below exchange
Come on! See you next time!
To share with you a few I wrote in front of a few SAO operation
Talk about different strategy patterns (Bookmarks)
Copy object, this operation is a little SAO!