This article mainly studies detailed explanation of Java programming Retry retry mechanism examples and shared relevant code examples. The editor thinks it is quite good and has certain reference value. Friends who need it can refer to it.
A function needs to be implemented in the application: the data needs to be uploaded to the remote storage service, and other operations are performed when the return processing is successful. This function is not complicated and is divided into two steps: the first step is to call the remote Rest service logic wrapper to return the processing result to the processing method; the second step is to get the result of the first step or catch the exception. If an error or exception occurs, the upload logic will be retryed, otherwise the logical operation will be continued.
Based on the normal upload logic, the function logic is executed by judging whether the return result or listening to the exception decision is retrying. At the same time, in order to solve the invalid execution of immediate retry (assuming that the exception is caused by external execution instability), the function logic is reexecution for a certain delay time.
public void commonRetry(Map<String, Object> dataMap) throws InterruptedException { Map<String, Object> paramMap = Maps.newHashMap(); paramMap.put("tableName", "creativeTable"); paramMap.put("ds", "20160220"); paramMap.put("dataMap", dataMap); boolean result = false; try { result = uploadToOdps(paramMap); if (!result) { Thread.sleep(1000); uploadToOdps(paramMap); //Try once} } catch (Exception e) { Thread.sleep(1000); uploadToOdps(paramMap); //Try once} }The above solution may still be invalid for retry. To solve this problem, try to increase the retry count and retry interval interval to achieve the possibility of increasing the retry valid.
public void commonRetry(Map<String, Object> dataMap) throws InterruptedException { Map<String, Object> paramMap = Maps.newHashMap(); paramMap.put("tableName", "creativeTable"); paramMap.put("ds", "20160220"); paramMap.put("dataMap", dataMap); boolean result = false; try { result = uploadToOdps(paramMap); if (!result) { reuploadToOdps(paramMap,1000L,10);//Delay multiple retry} } catch (Exception e) { reuploadToOdps(paramMap,1000L,10);//Delay multiple retry} }There is a problem with Solution 1 and Solution 2: Normal logic and retry logic are strongly coupled. Retry logic relies very much on the execution results of normal logic, and passive retry triggers for the expected results of normal logic. The root cause of retry is often overwhelmed by the complex logic, which may lead to inconsistent understanding of what problems to solve for subsequent operations and maintenance. Retrying is difficult to guarantee and is not conducive to operation and maintenance, because retrying design relies on normal logic exceptions or retrying the root cause of the conjecture.
So is there a solution that can be used to decouple normal logic and retry logic, and at the same time, it can give retry logic a standardized solution? The answer is: that is, a retry tool based on the agent design pattern. We try to use the corresponding tool to reconstruct the above scenario.
The specific definition of the command design pattern is not explained. The main reason is that the command pattern can complete the interface operation logic by executing the object, and at the same time, the internal encapsulation of the retry logic is not exposed to the implementation details. For the caller, it is the execution of normal logic and achieve the goal of decoupling. Please see the specific function implementation. (Class diagram structure)
IRetry agrees on the upload and retry interface, which implements OdpsRetry-like encapsulates ODPS upload logic, and encapsulates the retry mechanism and retry strategy at the same time. At the same time, use the recovery method to perform the recovery operation at the end.
Our caller LogicClient does not need to pay attention to retry. It implements the convention interface function through the retryer Retryer. At the same time, Retryer needs to respond and process the retry logic. The specific retry processing of the Retryer is handed over to the real IRtry interface implementation class OdpsRetry. By adopting the command mode, the normal logic and retry logic are separated gracefully, and at the same time, the normal logic and retry logic are separated by building the role of retryers, so that retry has better scalability.
spring-retry is an open source toolkit, currently available version 1.1.2.RELEASE, which customizes the retry operation template, and can set retry policies and fallback policies. At the same time, retry the execution instance to ensure thread safety. The specific operation examples are as follows:
public void upload(final Map<String, Object> map) throws Exception { // Build a retry template instance RetryTemplate retryTemplate = new RetryTemplate(); // Set the retry policy, mainly set the number of retries SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)); // Set the retry fallback operation policy, mainly set the retry interval FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(100); retryTemplate.setRetryPolicy(policy); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); //RetryCallback retry the callback instance to wrap the normal logic logic, the first execution and retry execution are all this logic final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() { //RetryContext retry the operation context convention, unify spring-try wrapping public Object doWithRetry(RetryContext context) throws Exception { System.out.println("do something"); Exception e = uploadToOdps(map); System.out.println(context.getRetryCount()); throw e;// Pay special attention to this point. The root of retry is returned through Exception} }; // Retrieve the retry process normally ends or reaches the retry upper limit. Final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() { public Object recover(RetryContext context) throws Exception { System.out.println("do recovery operation"); return null; } }; try { // Execute the execute method by retryTemplate to start logical execution of retryTemplate.execute(retryCallback, recoveryCallback); } catch (Exception e) { e.printStackTrace(); } }After a brief analysis of the case code, RetryTemplate assumes the role of retry executor. It can set SimpleRetryPolicy (retry policy, set retry upper limit, retry root entity), FixedBackOffPolicy (fixed fallback policy, set the time interval for retry fallback). RetryTemplate executes operations through execution, and two class instances, RetryCallback and RecoveryCallback, are required to prepare two class instances. The former corresponds to the retry callback logic instance and wraps normal functional operations. RecoveryCallback implements the recovery operation instance at the end of the entire execution operation.
The execution of RetryTemplate is thread-safe, and the implementation logic uses ThreadLocal to save the RetryContext execution context of each execution instance.
Although the Spring-retry tool can elegantly implement retry, there are two unfriendly designs: one is that the retry entity is limited to the Throwable subclass, indicating that the retry is aimed at snappy functional exceptions as the design premise, but we want to rely on a data object entity as the retry entity, but the Spring-retry framework must be cast to the Throwable subclass. Another is the assertion object that retrys the root cause uses the Exception exception instance of doWithRetry, which does not conform to the return design of normal internal assertions.
Spring Retry advocates retrying methods in annotation. The retry logic is executed synchronously. The "failure" of retry is targeted at Throwable. If you want to determine whether you need to retry based on a certain state of the return value, you may only be able to judge the return value yourself and then explicitly throw an exception.
Spring's abstraction for Retry
"Abstract" is a necessary quality for every programmer. For me with mediocre qualifications, there is no better way to improve than imitating and understanding excellent source codes. To do this, I rewrite its core logic... Let's take a look at Spring Retry's abstraction for "retry".
Spring retry related interface.jpg
The Guava retryer tool is similar to spring-retry. It wraps normal logical retry by defining the role of the retryer. However, Guava retryer has a better policy definition. On the basis of supporting the number of retry times and retry frequency control, it can be compatible with the retry source definition that supports multiple exceptions or custom entity objects, giving the retry function more flexibility. Guava Retryer is also thread-safe. The entry call logic uses the call method of Java.util.concurrent.Callable. The sample code is as follows:
public void uploadOdps(final Map<String, Object> map) { // RetryerBuilder Build a retry instance retryer, you can set the retry source and support multiple retry sources, you can configure the number of retry times or retry timeout time, and you can configure the waiting time interval Retryer<Boolean> retryer = RetryerBuilder.<Boolean> newBuilder() .retryIfException().//Set exception retry source retryIfResult(new Predicate<Boolean>() {//Set custom segment retry source, @Override public boolean apply(Boolean state) {//Special note: This apply returns true, which means that retry needs to be differentiated from the semantics of the operation logic. Return true; } }) .withStopStrategy(StopStrategies.stopAfterAttempt(5))//Set retry 5 times, and you can also set the retry timeout time.withWaitStrategy(WaitStrategies.fixedWait(100L, TimeUnit.MILLISECONDS)).build();//Set each retry interval try { //The retry portal uses the call method, using the call method of java.util.concurrent.Callable<V>, so the execution is thread-safe boolean result = retryer.call(new Callable<Boolean>() { @Override public Boolean call() throws Exception { try { //Special note: Return false statement does not require retry, return true statement needs to continue retry return uploadToOdps(map); } catch (Exception e) { throw new Exception(e); } } }); } catch (ExecutionException e) { } catch (RetryException ex) { } }Sample code principle analysis:
RetryerBuilder is a factory creator that can customize the retry source and can support multiple retry sources, can configure the number of retry times or retry timeout, and can configure the waiting time interval to create a retryer Retryer instance.
The retry source of RetryerBuilder supports Exception exception objects and custom assertion objects, and is set through retryIfException and retryIfResult, it supports multiple and is compatible at the same time.
The waiting time and retry limit configuration of RetryerBuilder are implemented using different policy classes, and the waiting time features can support uninterval and fixed interval modes.
Retryer is an instance of the retryer, which executes the operation logic through the call method, and encapsulates the retry source operation.
Elegant retry commonality and principle
Normal and retry elegantly decoupled, retry asserting that conditional instances or logical exception instances are the mediums for the communication between the two.
Agree on retry intervals, differential retry strategies, and set retry timeout time to further ensure the effectiveness of retry and the stability of the retry process.
All use the command design pattern, and the corresponding logical operations are completed by delegating the retry object, and the retry logic is implemented internally.
Spring-tryer and guava-tryer tools are both thread-safe retry and can support the correctness of retry logic in concurrent business scenarios.
Applicable scenarios for retry gracefully
There are unstable dependency scenarios in the functional logic, and it is necessary to use retry to obtain the expected result or try to reexecute the logic without ending immediately. For example, remote interface access, data load access, data upload verification, etc.
There are scenarios that need to be retryed for exception scenarios, and at the same time, it is hoped to decouple the normal logic and the retry logic.
For interactions based on data media, retry schemes can also be considered in scenarios that require retry polling to detect execution logic.
The above is all the detailed explanation of the Java programming Retry retry mechanism example. I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!