What's the difference between #CircuitBreaker from spring-retry and #HystrixCommand from spring-cloud-starter-netflix-hystrix?
They both seem to implement the circuit breaker pattern.
And the offical Spring guide is using #HystrixCommand: https://spring.io/guides/gs/circuit-breaker/
In their example:
#HystrixCommand(fallbackMethod = "reliable")
public String readingList() {
URI uri = URI.create("http://localhost:8090/recommended");
return this.restTemplate.getForObject(uri, String.class);
}
public String reliable() {
return "Cloud Native Java (O'Reilly)";
}
Is equivalent (as far as I can tell) to:
#CircuitBreaker
public String readingList() {
URI uri = URI.create("http://localhost:8090/recommended");
return this.restTemplate.getForObject(uri, String.class);
}
#Recover
public String reliable() {
return "Cloud Native Java (O'Reilly)";
}
#CircuitBreaker- Not sure if it an actual annotation. AFAIK, it is not.
spring-retry- #Retryable is the annotation used to achieve this. What this basically does is retry the current method being executed a specified number of times based upon certain criteria.
#HystrixCommand- This annotation actually triggers the Circuit Breaker functionality when a method is executed.
Effective differences-
#Retryable has nothing to do with Circuit Breaking. It blindly retries the operation even if the result is a failure everytime. It operates on a local context of the current request. What that means it is not aware about the global state of retries on your system. For all it cares is that every request must be getting retried a bunch of times.
#HystrixCommand is a smart annotation. It is aware of the global state of the system and actually stops executing the method if the number of failures breach the Circuit Breaking threshold specified.
Related
Lets say webServiceA is calling webServiceB's Endpoint1 and Endpoint2.
Now Endpoint1 starts sending an error response, and webservice A is using a Hystrix circuit breaker. Will the circuit be in OPEN state for just Endpoint1 or for all endpoints from webServiceA to webServiceB ?
Hystrix is not aware of any web service connection, it can be used for anything. For example, if you have a slow DB connection, you can open the circuit for that.
So the answer is no, it will not OPEN a circuit for all endpoints out of the box. However, you can setup your service implementation to behave in that way.
For example:
#Service
public class WebServiceA {
#CircuitBreaker
public ResponseEntity<?> call(String url, String param) {
// implementation here
restTemplate.exchange("http://webserviceA/" + url, ...);
}
}
And the method call() will be the only public access level method to access WebServiceA.
I am using Spring Boot 1.5, and I have a controller that executes asynchronously, returning a CompletableFuture<User>.
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private final UserService service;
#GetMapping("/{id}/address")
public CompletableFuture<Address> getAddress(#PathVariable String id) {
return service.findById(id).thenApply(User::getAddress);
}
}
The method UserService.findById can throw a UserNotFoundException. So, I develop dedicated controller advice.
#ControllerAdvice(assignableTypes = UserController .class)
public class UserExceptionAdvice {
#ExceptionHandler(UserNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
#ResponseBody
public String handleUserNotFoundException(UserNotFoundException ex) {
return ex.getMessage();
}
}
The problem is that tests are not passing returning an HTTP 500 status and not a 404 status in case of an unknown user request to the controller.
What's going on?
The problem is due to how a completed exceptionally CompletableFuture handles the exception in subsequent stages.
As stated in the CompletableFuture javadoc
[..] if a stage's computation terminates abruptly with an (unchecked) exception or error, then all dependent stages requiring its completion complete exceptionally as well, with a CompletionException holding the exception as its cause. [..]
In my case, the thenApply method creates a new instance of CompletionStage that wraps with a CompletionException the original UserNotFoundException :(
Sadly, the controller advice does not perform any unwrapping operation. Zalando developers also found this problem: Async CompletableFuture append errors
So, it seems to be not a good idea to use CompletableFuture and controller advice to implement asynchronous controllers in Spring.
A partial solution is to remap a CompletableFuture<T> to a DeferredResult<T>. In this blog, an implementation of a possible Adapter was given.
public class DeferredResults {
private DeferredResults() {}
public static <T> DeferredResult<T> from(final CompletableFuture<T> future) {
final DeferredResult<T> deferred = new DeferredResult<>();
future.thenAccept(deferred::setResult);
future.exceptionally(ex -> {
if (ex instanceof CompletionException) {
deferred.setErrorResult(ex.getCause());
} else {
deferred.setErrorResult(ex);
}
return null;
});
return deferred;
}
}
So, my original controller would change to the following.
#GetMapping("/{id}/address")
public DeferredResult<Address> getAddress(#PathVariable String id) {
return DeferredResults.from(service.findById(id).thenApply(User::getAddress));
}
I cannot understand why Spring natively supports CompletableFuture as return values of a controller, but it does not handle correctly in controller advice classes.
Hope it helps.
For those of you who still run into trouble with this : even though Spring correctly unwraps the ExecutionException, it doesn't work if you have a handler for the type "Exception", which gets chosen to handle ExecutionException, and not the handler for the underlying cause.
The solution : create a second ControllerAdvice with the "Exception" handler, and put #Order(Ordered.HIGHEST_PRECEDENCE) on your regular handler. That way, your regular handler will go first, and your second ControllerAdvice will act as a catch all.
I have two spring boot services.
A and B.
From A service, I am calling B Service(Using Feign Client).
I have one request interceptor which adds custom headers to the request before sending it.
This is my interceptor:
public class HeaderInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate template) {
try {
Object encrypyedData = RequestContextHolder.currentRequestAttributes().getAttribute(""headerName", 0);
if(encrypyedData != null) {
template.header("headerName", encrypyedData.toString());
}else {
System.out.println("Encrypted Data is NULL");
}
} catch(Exception e) {
System.out.println("ankit === "+e.getMessage());
}
}
}
But when I am running this code, I am getting exception : No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
I have also tried adding this in Service A
#Bean public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
I have added this in Main Application File(file annotated with #SpringBootApplication).
Still the same issue. What am i missing?
Probably you're running your spring cloud feign on Hystrix.
Hystrix's default isolation mode is thread, so actual HTTP request will be called on the different thread that is managed by hystrix. And HeaderInterceptor also run on that thread.
But, you are accessing RequestContextHolder and it is using ThreadLocal. That is probably the reason for your error.
Try to use one of the the below properties on A service.
It will disable hystrix for feign.
feign:
hystrix:
enabled: false
Or, you can just change isolation mode of your hystrix like below.
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
If it works, you need to choose the way that you apply to. Just disable hystrix or change isolation strategy or just passing attribute via another way.
I have created a Spring Boot 2 demo application with the Spring Initializr and added the controller below:
#Controller
#RequestMapping("/demo")
public class UploadController {
private final static Logger LOG = LoggerFactory.getLogger(UploadController.class);
#PostMapping("/upload")
public ResponseEntity<String> uploadFile(
#RequestParam("metadata") MultipartFile metadata,
#RequestParam("payload") MultipartFile payload) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Map metadataMap = mapper.readValue(metadata.getInputStream(), Map.class);
LOG.info("Received call to upload file {}", metadataMap.get("filename"));
LOG.info("File size: {}", payload.getBytes().length);
LOG.info("File {} successfully uploaded", metadataMap.get("filename"));
return ResponseEntity.ok().build();
}
}
I then added an application.yaml file containing this configuration:
spring:
servlet:
multipart:
max-file-size: 2000000MB
max-request-size: 2000000MB
resolve-lazily: true
My goal is to have the controller parse and log the metadata file before it starts reading the payload file, but the resolve-lazily setting seems to be ignored by Boot: the code inside the controller won't be executed until the whole body is read.
I use the command below to test the controller:
curl -F metadata=#metadata.json -F payload=#payload.bin http://localhost:8080/demo/upload
Is there anything wrong with my code/configuration? Am I getting the meaning of the setting right?
At present, if you want to avoid reading (and buffering) the whole body all at once, I think you will have to provide your own parser, as described in the answers here. What would be really interesting (but generally unnecessary) would be to do so in the form of a new MultipartResolver implementation.
There are two existing implementations documented for interface MultipartResolver, and both supply a function setResolveLazily(boolean) (standard), (commons). I have tried with both, and neither seem to allow for parsing or streaming multipart files or parameters independently.
Default is "false", resolving the multipart elements immediately, throwing corresponding exceptions at the time of the resolveMultipart(javax.servlet.http.HttpServletRequest) call. Switch this to "true" for lazy multipart parsing, throwing parse exceptions once the application attempts to obtain multipart files or parameters.
Despite what it says in the documentation, I have found that once you call resolveMultipart, the entire body is parsed and buffered before the call returns. I know this because I can watch the temp-files being created.
One note about "Is there anything wrong with my code"...
Answer: Yes, because by using #RequestParam you have indirectly asked Spring to resolve your parameters ahead of time, before your controller is ever called. What you should be able to do instead (if the documentation were correct) is request the parameters independently from inside your controller:
Configuration (application.properties):
spring.servlet.multipart.enabled = true
spring.servlet.multipart.resolve-lazily = true
Controller:
#PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> postUpload(HttpServletRequest rawRequest) {
multipartResolver.setResolveLazily(true); // unclear why this is exists
MultipartHttpServletRequest request = multipartResolver.resolveMultipart(rawRequest);
String p1 = request.getParameter("first-parameter");
String p2 = request.getParameter("second-parameter");
System.out.println("first-parameter="+p1+", second-parameter"+p2);
multipartResolver.cleanupMultipart(request);
return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
}
One useful aspect of resolve-lazily that I have discovered is that it allows you to write your own parser for some rest controllers while using the built-in parser for others (see my answer here). In other words, you don't have to use spring.servlet.multipart.enabled = false to get your parser to work. This is a minor breakthrough relative to other advice that I had seen previously.
I have a Spring controller that is currently being accessed normally, but i want to change the implementation in such a way that if the task the controller is performing takes more than a defined time for instance 10 seconds then the controller can respond with a "your request is being processed message" to the caller, but if the method returns within time then the response is passed to the calling method from the controller, in other words i want timed asynchronous execution from a Spring controller.
NB: This is not exactly TaskExecutor domain(at least according to my understanding) because i don't want to just hand over execution to the TaskExecutor and return immediately.
I use Spring 3.0 and Java 1.5 and the controllers don't have views, i just want to write the output direct to stream, which the calling client expects.
Well, it is the TaskExecutor domain. In your controller simply wrap your processing logic in Callable, submit it to the AsyncTaskExecutor and wait up to 10 seconds. That's it!
final Future<ModelAndView> future = asyncTaskExecutor.submit(new Callable<ModelAndView>() {
#Override
public ModelAndView call() throws Exception {
//lengthy computations...
return new ModelAndView("done");
}
});
try {
return future.get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
return new ModelAndView("timeout");
}
Of course this is a bit nasty, especially when it occurs more than once in one controller. If this is the case, you should have a look at servlet 3.0 asynchronous support (see below).
Further reading:
25. Task Execution and Scheduling
How to use Servlet 3 #WebServlet & async with Spring MVC 3?
Support Servlet 3.0 (JSR-315)