I followed https://resilience4j.readme.io/docs/getting-started-3 for Resilience4J documentation.
I have a problem, my circuitbreaker is immediately connecting to the fallback( on the first invoke) when the primary-backend is not available.
#Bean
public CircuitBreakerConfig circuitBreakerConfig() {
return CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.slidingWindowSize(5)
.minimumNumberOfCalls(5)
.failureRateThreshold(4)
.build();
}
I also tried moving the config to application.yml, but still the same behavior.
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
#CircuitBreaker(name = "backendA", fallbackMethod = "fallbackA")
public String backendA() {
return restTemplate.getForObject("http://localhost:9999/backendA", String.class);
}
public String fallbackA(Exception e) {
return restTemplate.getForObject("http://localhost:8080/partner", String.class);
}
Can someone help?
//UPDATE
More details below.
As shown in the code above, my app is making a call to /backendA, behind a circuit breaker. It is expected that as per config, if backendA is not available, the should fail 5 times and 6th call onwards fallback to /partner API. Or in other words, circuit should open after 5 calls.
This is how I tested.
I started the application. Both backendA and fallbackA are available. After a few calls, I killed backendA. the next call to backendA is falling back to /partner while I expect the next 5 calls to backendA fails without a fallback. Is my expectation correct?
After some research, I came to know that fallback on intermittent failures is an expected behaviour, though I could not find it documented anywhere. So even if the circuit is CLOSED, fallback will be invoked if the method throws a matching exception.
Note:
If you are not happy with the immediate fallback, wrap it with a RETRY config.
#CircuitBreaker- you should expect the result that if it's status is open that you will immediately receive a fallback and it no longer calls your desired service.
If you expect the result that when you turn off your called backend service and you want 5 call until you get fallback you need use #Retry, if the call fails, then it will try to call your service again.
If you use multiple notations, you can read more about the order here
Related
Here I've three subflows and out of which one is HTTP outbound call. I want that HTTP call should try to get response till a mentioned time. If times out then the main flow should break and it should show a Error message in Json format as output.
Below is the code -
#Bean
public IntegrationFlow flow() {
return flow ->
flow.handle(validatorService, "validateRequest")
.split()
.channel(c -> c.executor(Executors.newCachedThreadPool()))
.scatterGather(
scatterer ->
scatterer
.applySequence(true)
.recipientFlow(flow1())
.recipientFlow(
f ->
f.gateway(
flow2(), gateway -> gateway.replyTimeout(3000L).errorChannel("errorChannel")))
.recipientFlow(flow3()),
gatherer ->
gatherer
.releaseLockBeforeSend(true)
.releaseStrategy(group -> group.size() == 2))
.aggregate(someMethod1())
.to(someMethod2());
}
private IntegrationFlow returnError() {
return IntegrationFlows.from("errorChannel").handle(System.out::println).get();
}
I've added the errorChannel but how do I send a customized message to the user?
See documentation for error handling in the messaging gateway: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#gateway-no-response.
Consider to add an errorChannel() along side with that replyTimeout() on the gateway definition to build an error reply you'd like. However you also may consider to add something like a request timeout for the RestTemplate you use for that HTTP call to prevent the long wait for HTTP response.
UPDATE
First of all you need to understand that request-reply is a bit of block-n-then-wait approach. We send a request and if the process consuming that message is blocking - performed immediately in a thread producing the message, then we don't reach "wait" part until that process lets go. In most cases (by default) a DirectChannel is used, so it is blocked because it is performed in the calling thread. This process might be your HTTP call which is also request-response pattern. So, we don't reach that "wait" part until this HTTP call returns, or timeout, or fail with error. Only after that a replyTimeout takes its effect to wait for the reply from the underlying process. This can be changes if an input channel of that process is not direct. See an ExecutorChannel or QueueChannel. This way a sending part exits immediately because there is nothing to block it and it goes to the "wait" part to observe a CountDownLatch.
So, you need to think again if that replyTimeout() option is appropriate for your or not. Perhaps the mentioned requestTimeout for the RestTemplate is better option for you, than rework your flow to the async solution to leverage that replyTimeout() feature. Again: see the documentation I've mentioned about that replyTimeout feature.
The error handling is described here: https://docs.spring.io/spring-integration/docs/current/reference/html/error-handling.html#error-handling.
It is really not recommended to rely on the global errorChannel bean. This is one which is used everywhere in async processes where there is no an explicit error channel configured.
You said in your question "send a customized message to the user", but your error handling flow is one-way - System.out::println. If you want to return anything from the error handling flow, the endpoint must be replying one, e.g.:
.handle((p, h) -> "The error during HTTP call: " + p)
Also see if you declare that returnError() correctly. It really cannot be just plain private method. The IntegrationFlow must be declared as a bean this or other way to initiate wiring process for endpoints and channels. Right now that one is just a plain, unused private method. The framework does not see that method to do anything. See basics of the Java DSL in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/dsl.html#java-dsl
I am working on a fallback procedure for when the connection fail (or another error) occurs. I've created the CacheConfiguration/CacheErrorHandler to handle the errors and log them. The application successfully switches between using the cache and going through the normal process when Redis fails.
However, the way I've implemented cache eviction endpoint (via the #cacheEvict annotation), it is essentially an empty method.
#DeleteMapping(value = "/cache/clear")
#CacheEvict(value = {_values_}, allEntries = true)
public ResponseEntity<String> clearAllCache() {return ResponseEntity.ok("OK"); }
Current CacheErrorHandler
#Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler() {
#Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
LOGGER.warn("Failure evicting from cache: " + cache.getName() + ", exception: " + exception);
}
}
Logger will output the cacheEvictError but the response will send back "OK" to the client.
Is there a way to catch the cache error and send a different response saying that the cache evict failed?
I've tried adding a try-catch to throw an exception inside the endpoint but that went nowhere. Couldn't seem to find any examples online to solve this specific issue.
One thing to keep in mind here is that Spring's #CacheEvict annotation and behavior is called "after" the method (by default) on which the annotation is declared, which in your case is the clearAllCache() Web service method.
Although, you can configure the cache eviction to occur before the (actual) clearAllCache() Web service method is called, like so:
#CacheEvict(cacheNames = { ... }, allEntries = true, beforeInvocation = true)
public ResponseEntity<String> clearAllCache() {
// ...
}
That is, using the beforeInvocation attribute on the #CacheEvict annotation, set to true, the cache eviction (for all entries) will occur before the actual clearAllCache() method is invoked.
NOTE: Logically, if the invocation happens after the clearAllCache() method has already been called, then you really have no way to respond if the cache eviction (or rather, the "clear" operation) was unsuccessful. So you must configure the cache eviction to occur before your Web service method gets invoked, first of all.
Next, you need someway to know that your custom CacheErrorHandler was invoked on an error occurring in your caching provider (e.g. Redis) during eviction (or technically, the Cache.clear() operation in this case, since you evicting "all entries").
Another thing to keep in mind here is that since you appear to be operating in Web environment (e.g. a Servlet container like Tomcat or Jetty, or other) then you need to keep "Thread Safety" in mind since each HTTP request and corresponding Web handler method, like the clearAllCache() method called on HTTP DELETE, will be invoked from a separate Thread (i.e. Thread per (HTTP) Request model).
So, you can solve that problem using a Java ThreadLocal declared inside your custom CacheErrorHandler class to capture the necessary state / information that is needed once the clearAllCache() method is called.
I have wrote one such example test class demonstrating how you could accomplish this. The key to this implementation (solution) is the proper configuration of the cache eviction and the use of the ThreadLocal in the custom CacheErrorHandler.
My test is not specifically configured as a Web-based service (e.g. using Spring Web MVC, or anything like that), but I modeled the test use case after your particular situation. I also made use of Mockito to spy on the Spring caching infrastructure to always throw a RuntimeException anytime a Cache eviction based operation occurs (e.g. evict(key) or clear(), etc).
Of course, there are probably better, more robust ways to implement this solution, but this at least demonstrates that it is possible.
Hopefully, this gives you more ideas.
I'm writing a Web application that makes downstream calls using RestTemplate. If the underlying service returns a 401 Unauthorized, I want to also return a 401 to the calling application; the default behavior is to return a 500. I want to keep the default Spring Boot error response as provided by BasicErrorController; the only change I want is to set the status code.
In custom exceptions, I'd just annotate the exception class with #ResponseStatus, but I can't do that here because HttpClientErrorException.Unauthorized is provided by Spring. I tried two approaches with #ControllerAdvice:
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void returnsEmptyBody(HttpClientErrorException.Unauthorized ex) {
}
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void doesNotUseBasicErrorController(HttpClientErrorException.Unauthorized ex) {
throw new RuntimeException(ex);
}
How can I configure MVC to continue to use all of the built-in Boot error handling except for explicitly overriding the status code?
The below code works for me -- in an app consisting of a #RestController whose one method consisted of throw new HttpClientException(HttpStatus.UNAUTHORIZED), running on an embedded Tomcat. If you're running on a non-embedded Tomcat (or, I suspect, on an embedded non-Tomcat) odds are you'll have to do something at least somewhat different, but I hope this answer is at least somewhat helpful anyway.
#ControllerAdvice
public class Advisor {
#ExceptionHandler(HttpClientException.class)
public String handleUnauthorizedFromApi(HttpClientException ex, HttpServletRequest req) {
if (/* ex instanceof HttpClientException.Unauthorized or whatever */) {
req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 401);
}
return "forward:/error";
}
}
Explanation: when a HttpClientException is thrown while we're processing request X (in an embedded servlet), what normally happens is that it bubbles all the way up to some org.apache class. (I might fire the debugger up again and work out which one, but this is a pretty high-level explanation so it doesn't matter much.) That class then sends request X back to the application, except this time the request goes to "/error", not to wherever it was originally going. In a Spring Boot app (as long as you don't turn some autoconfiguration off), that means that request X is ultimately processed by some method in BasicErrorController.
OK, so why does this whole system send a 500 to the client unless we do something? Because that org.apache class mentioned above sets something on request X which says "processing this went wrong". It is right to do so: processing request X did, after all, result in an exception which the servlet container had to catch. As far as the container is concerned, the app messed up.
So we want to do a couple of things. First, we want the servlet container to not think we messed up. We achieve this by telling Spring to catch the exception before it reaches the container, ie by writing an #ExceptionHandler method. Second, we want the request to go to "/error" even though we caught the exception. We achieve this by the simple method of sending it there ourselves, via a forward. Third, we want the BasicErrorController to set the correct status and message on the response it sends. It turns out that BasicErrorController (working in tandem with its immediate superclass) looks at an attribute on the request to determine what status code to send to the client. (Figuring this out requires reading the class's source code, but that source code is on github and perfectly readable.) We therefore set that attribute.
EDIT: I got a bit carried away writing this and forgot to mention that I don't think using this code is good practice. It ties you to some implementation details of BasicErrorController, and it's just not the way that the Boot classes are expected to be used. Spring Boot generally assumes that you want it to handle your error completely or not at all; this is a reasonable assumption, too, since piecemeal error handling is generally not a great idea. My recommendation to you -- even if the code above (or something like it) does wind up working -- is to write an #ExceptionHandler that handles the error completely, meaning it sets both status and response body and doesn't forward to anything.
You can customize the error handler of the RestTemplate to throw your custom exception, and then handle that exception with the #ControllerAdvice as you mentioned.
Something like this:
#Configuration
public class RestConfig {
#Bean
public RestTemplate restTemplate(){
// Build rest template
RestTemplate res = new RestTemplate();
res.setErrorHandler(new MyResponseErrorHandler());
return res;
}
private class MyResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (HttpStatus.UNAUTHORIZED.equals(response.getStatusCode())) {
// Throw your custom exception here
}
}
}
}
I am writing a web application using Spring Boot that frequently updates data on the back end and returns the updated object to reflect the update on the front end.
The question I have is what to return from my methods if the update should fail for some reason.
I am currently returning the object as it was received should it fail but as it stands the state on the front end would not reflect the failure on the back end in the case that it occurs.
I want to return the object to update the state but doing so prevents me from returning a String or HttpStatus indicating a problem doesn't it? Returning the old object doesn't seem a good solution either.
You can throw an exception in this case of failure from your REST controller.
To handle this exception, Spring provides ResponseEntityExceptionHandler callback class with the help of which you can handle the thrown exception and set different headers in the response entity.
So on client-side, you can recognise that some failure is occurred on server side.
You can set HttpStatus as HttpStatus.INTERNAL_SERVER_ERROR and add more details in the body.
The question I have is what to return from my methods if the update should fail for some reason.
You first need to determine whether the error was caused by the client or by the server, then you can determine the most suitable status code to be returned, either in the 4xx or in the 5xx range. See this answer which may give you some insights.
Instead of returning the request request back in the response, you should return a payload that describes what the problem was. Consider, for example, the payload defined in the RFC 7807 along with the application/problem+json media type.
Finally, this answer may give you insights on how to map an exception to a HTTP status code in Spring:
You can map exceptions to responses by annotating an exception class with #ResponseStatus.
It also gives you the possibility to implement a HandlerExceptionResolver or extend one of the existing implementations, such as the AbstractHandlerExceptionResolver.
Another approach would be using a ResponseEntityExceptionHandler annotated with #ControllerAdvice and define the handled exceptions by annotating the implemented method with #ExceptionHandler.
I have the following bean:
#Bean
public MessageProducer sqsMessageAdapter() {
SqsMessageDrivenChannelAdapter adapter = new SqsMessageDrivenChannelAdapter(this.amazonSqs, awsConfiguration.myQueue.get());
adapter.setAutoStartup(true);
adapter.setMaxNumberOfMessages(1);
adapter.setSendTimeout(2000);
adapter.setVisibilityTimeout(200);
adapter.setWaitTimeOut(20);
adapter.setOutputChannel(this.myOutput);
return adapter;
}
MORE INFO:
I am trying to find a way of stopping/starting the polling on command, i'm doing this using annotations without xml.
Currently I am using the SqsMessageDrivenChannelAdapter and calling the stop method, followed by the start when I want to restart the channel adapter. The problem I am having is that a timeout exception gets thrown in the stop method regardless of timeout settings. This happens on the future.get() call (line 197 of SimpleMessageListenerContainer) I think this is causing start not to work. Start does not throw any exceptions but the poller does not pick up any new messages
UPDATE:
The start and stop commands seem to be working correctly. The problem seems to be that I cannot set the property:
public void setQueueStopTimeout(long queueStopTimeout) {
this.queueStopTimeout = queueStopTimeout;
}
This lives with SimpleMessageListenerContainer.java from SqsMessageDrivenChannelAdapter.java without this I am getting timeouts as the default is not long enough.
Your question isn't clear. Please, consider be more specific in the future.
Anyway let me guess, that you mean start()/stop() operations of that SqsMessageDrivenChannelAdapter. Not sure that what is the problem to inject it in the desired place and call those methods.
#Autowired
#Qualifier("sqsMessageAdapter")
private Lifecycle sqsMessageAdapter;