How to retry if web services are down? - java

I tried to retry when the web service failed.
I was using spring retry, but with spring retry I can't specify the status code based retry
This is my code
#retryable(value=Exception.class,maxAttampts=2)
Public void retry() throws exception {
System.out.println("retry started")
throw new exception}
I invoked the retry() method in my webservice class.

Spring retry has the following structure.
#Retryable(
value = {IllegalArgumentException.class},
maxAttempts = 5,
backoff = #Backoff(1000)
)
It will work when there is a specific exception. It does not work based upon the custom value. In your case, if the status code like 200 or 201 is not retrieved from a service, then you can throw an exception or your custom exception for which you can perform this retry operation. It works only on exceptional cases I mean for exception.Any value can be transformed into an exception to perform retry operation.
you can refer the following link.
https://github.com/debjava/spring-retry/blob/master/src/main/java/com/ddlab/rnd/MyServiceImpl.java

Related

Retrying with Spring Retry based on error message or status code?

I am using the spring-retry dependency and currently have a Java Spring Boot application class annotated with #EnableRetry, and a function annotated with the below
#Retryable(value = MongoException.class,
maxAttempts = 4, backoff = #Backoff(delay = 100))
This allows me to retry for all exceptions of type MongoException for the function. However, id like to retry based on a specific status code or even an error message that is thrown. Is this possible to achieve with Spring Retry?
In the #Retryable method, catch the exception; then:
RetrySynchronizationManager.getContext().setExhaustedOnly()`
and rethrow it (or another exception type).
will disable retries and go straight to the #Recover method, if supplied, otherwise it will be thrown to the caller.
Or, as indicated in #geobreze comment above, use an exceptionExpression https://docs.spring.io/spring-retry/docs/api/current/org/springframework/retry/annotation/Retryable.html#exceptionExpression--

How to handler errors/exceptions while using Spring Kafka framework?

I am not able to find how to do custom error handling in for a spring kafka consumer.
My requirement is:
For any deserialization errors, just write the error and message to database.
For any errors in execution under #KafkaListener method, retry 3 times and then write the error and message to database.
From the spring docs, I found that,
For 1, I will have to use ErrorHandlingDeserializer and it will then call the #KafkaListener error handler.
For 2, framework provides SeekToCurrentErrorHandler which handles message retries.
I am not understanding where can I add the code to write the exception/message to database in addition to enable configured retries.
Add a recoverer to the SeekToCurrentErrorHandler
new SeekToCurrentErrorHandler((rec, ex) -> {
Throwable cause = ex.getCause();
if (cause instanceof DeserializationException) {
...
}
else {
...
}, new FixedBackOff(2000L, 2L));
By default, deserialization exceptions are not retried; most others are retried before calling the recoverer.

Passing error messages from SSE (Webflux) Spring Boot app to Angular 7 frontend

My application is a Spring Boot app with several end points. We are trying to add an SSE enabled endpoint, using Webflux.
Use case :
Step 1: Front-end submits a request to a POST endpoint and gets a unique ID.
Step 2: Front end fetches processed result using a GET endpoint (SSE enabled - Flux)
Angular uses EventSource object to consume the SSE endpoint. It requires the end point to produce text/event-stream. It works very well for positive test cases. But when runtime exceptions are thrown from the service, Angular frontend cannot get the HTTP status codes and exception details. It just errors with no data.
Thrown exceptions:
#ResponseStatus(code = HttpStatus.NOT_FOUND)
public class RequestNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public RequestNotFoundException(String message) {
super(message);
}
}
As mentioned in this, returning a Flux<ServerSentEvent> and terminating the flux with a custom event:
Java endpoint
#GetMapping(path= "{id}/async", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<Optional<ProcessedResult>>> getProcessedResultStream(
#PathVariable("id") #ApiParam(value = "The unique identifier of the request", required = true) String id)
throws InterruptedException {
// Combining data and adding a disconnect event to handle at the frontend.
return Flux.concat(
Flux.just(ServerSentEvent.<Optional<ProcessedResult>>builder(service.getProcessedResult(id))
.build()),
Flux.just(ServerSentEvent.<Optional<ProcessedResult>>builder().event("disconnect").build()));
}
This helps to close the connection at the UI end by disconnecting it. But my main issue remains open. How should I go about propagating error messages back to the frontend ? All other (normal) endpoints throw custom runtime exceptions with proper HTTP codes as given above.
Should I change my ProcessedResult class to take exception details also ?
Server side events use an open HTTP connection to stream content from the server. According to W3C spec of Eventsource HTTP 200 OK responses with a proper Content-Type header is required to establish the connection, HTTP 500, 502, 503, 504 or any other network errors will cause the browser to reestablish the connection. If none of the above cases is satisfied an error event is triggered with readyState = 'CLOSED'. In short, the onError callback is only for handling network timeouts or access control issues.
It is not possible to send a different HTTP status code based on runtime exception since you already send 200 Ok when you establish the connection. The only way to handle runtime error is to catch the exception and send a custom named event from the server and close the EvetSource based on that.

Feign: Retry depending on response status

I am currently using Spring Cloud and Feign to consume a Microservice in my application. Since it can happen, that a database connection or the like fails in a single service instance, making it return 500 HTTP status code, I want to make sure, that the next server is retried by the service's clients. Currently, Ribbon's retry mechanism works like a charm when the service is not running at all, however it still returns instantly an error when it receives a 500 status code, without any retry.
Is it possible to configure the Feign clients or their underlying Ribbon load balancers to retry the next server, if an instance returns a 500 response?
The configuration is pretty much the same as in this thread: Does Feign retry require some sort of configuration?
I would love to use an implementation like Ribbons' HttpResponseValidator (https://github.com/Netflix/ribbon/blob/master/ribbon/src/main/java/com/netflix/ribbon/http/HttpResponseValidator.java), but I couldn't find anything usable for Spring Cloud and its Feign/Ribbon integration
This question is very old and the solution was probably already found or wasn't possible at the time. Anyway, I think that answer might still help someone 8 ).
Please use this as a reference, this code is not intended for production use.
Feign allows you to configure errorDecoder - this is the place where magic happens.
Feign.Builder builder = Feign.builder()
.errorDecoder(new RetryOnScaleErrorDecoder())
Here is the implementation, I use that class to retry request on HTTP error 429 I get from AWS when service is scaling
public static class RetryOnScaleErrorDecoder implements ErrorDecoder {
#Override
public Exception decode(String methodKey, Response response) {
FeignException exception = errorStatus(methodKey, response);
// This is a terrible part please check how feign.codec.ErrorDecoder.RetryAfterDecoder is implemented for proper parsing of retry-after header
Collection<String> headers = response.headers().get("Retry-After");
String repeatAfterString = "0";
if (Objects.nonNull(headers)) {
repeatAfterString = Iterables.getFirst(headers, "0");
}
assert repeatAfterString != null;
Date repeatAfter = new Date(currentTimeMillis());
if (repeatAfterString.matches("^[0-9]+$")) {
try {
long deltaMillis = SECONDS.toMillis(Long.parseLong(repeatAfterString));
repeatAfter = new Date(currentTimeMillis() + deltaMillis);
} catch (NumberFormatException ignored) {
// TODO: logging
}
}
// That's the part where we decide to retry based on status code value
if (exception.status() == 429) {
return new RetryableException(
response.status(),
exception.getMessage(),
response.request().httpMethod(),
exception,
repeatAfter
);
}
return exception;
}
}
I think that in conjunction with Ribbon it will produce desired result.
Try to this config:
MY-SPRING-API.ribbon.retryableStatusCodes=404,500
This is the same question:
Feign client and Spring retry
document is :
https://docs.spring.io/spring-cloud-netflix/docs/2.2.10.RELEASE/reference/html/#retrying-failed-requests

Spring #Transaction annotation and Exception Handling

Consider the following code snippet. (I am using Spring 3.1 and Hibernate 3.6)
#Override
#Transactional
public <T extends Termination> void progressToPendingStage(Class<T> entity,
Long terminationId, String userName) throws Exception {
Termination termination = findTerminationById(entity, terminationId);
//TODO improvise such that email does not get sent if data is not saved
if (termination.getStatus().equals(TerminationStatus.BEING_PREPARED.toString())) {
termination.setStatus(TerminationStatus.PENDING.toString());
termination.setSubmittedDate(new Date());
termination.setSubmittedBy(userName);
saveOrUpdateTermination(termination);
//Send an email to SAS
emailHelper.configureEmailAndSend(termination);
}
}
Unit tests for the above method indicate that email will be sent regardless that the saveOrUpdateTermination(termination) throws an exception or not. On further testing and some research I have uncovered that this behavior is the expected behavior. This is not what the business rules desire. An email should be sent only if the termination record was saved successfully. Any suggestions on how to make this behave in the desired manner? One way I can think of is to make the caller handle the exception thrown by the progressToPendingStage method and if no exception was thrown send an email. Am I on the right track or can we alter the way #Transaction behaves.
I have solved this issue by designing around the problem. Sending an Email was never meant to be part of the transaction. I created an object that performed post saving tasks. The object will catch the exception thrown upon saving the termination and if no exceptions were thrown I would then trigger an email to be sent out. One could also put this in an Spring Aspect which could be executed upon successfully returning after a successful save.
Lessons learn't: Don't include steps that don't belong in a method marked with #transaction. If its included in a transaction Spring will silently handle the exception and not throw the exception till the transaction is finished. In short if a method is annotated with #Transaction every line in that method will be execute even though a line in the middle of the method throws an exception.

Categories

Resources