Zuul proxy server throwing Internal Server Error when request takes more time to process - microservices

Zuul Proxy Error
I am getting this error when the request takes more time to process in the service.But Zuul returns response of Internal Server Error
Using zuul 2.0.0.RC2 release

As far as I understand, in case of a service not responding, a missing route, etc. you can setup the /error endpoint to deliver a custom response to the user.
For example:
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping(value = "/error", produces = "application/json")
public #ResponseBody
ResponseEntity error(HttpServletRequest request) {
// consider putting these in a try catch
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
Throwable exception = (Throwable)request.getAttribute("javax.servlet.error.exception");
// maybe add some error logging here, e.g. original status code, exception, traceid, etc.
// consider a better error to the user here
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{'message':'some error happened', 'trace_id':'some-trace-id-here'}");
}
#Override
public String getErrorPath() {
return "/error";
}
}

Related

CircuitBreaker Fallback method not working

I have the below code in Billing service microservice:
#RestController
#RequestMapping("/billing")
public class WebController {
#Autowired
private BillingService service;
#GetMapping("/hi")
#CircuitBreaker(name="BillingServiceCapture", fallbackMethod = "hiFallback")
public String hi() {
return "Hello Khushboo!";
}
public String hiFallback() {
return "Hello Khushboo FallBack!";
}
Application.Properties file:
server.port=9191
spring.h2.console.enable=true
spring.application.name=billing-service
eureka.client.serviceurl.defaultzone=http://localhost:8761/eureka
eureka.instance.hostname=localhost
management.health.circuitbreakers.enabled=true
#actuator settings
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
resilience4j.circuitbreaker.instances.BillingServiceCapture.registerHealthIndicator=true
resilience4j.circuitbreaker.instances.BillingServiceCapture.eventConsumerBufferSize=10
resilience4j.circuitbreaker.instances.BillingServiceCapture.failureRateThreshold=20
resilience4j.circuitbreaker.instances.BillingServiceCapture.minimumNumberOfCalls=5
resilience4j.circuitbreaker.instances.BillingServiceCapture.automaticTransitionFromOpenToHalfOpenEnabled=true
resilience4j.circuitbreaker.instances.BillingServiceCapture.waitDurationInOpenState=5s
resilience4j.circuitbreaker.instances.BillingServiceCapture.permittedNumberOfCallsInHalfOpenState=3
resilience4j.circuitbreaker.instances.BillingServiceCapture.slidingWindowSize=10
resilience4j.circuitbreaker.instances.BillingServiceCapture.slidingWindowType=COUNT_BASED
However, if I send a Get Request: localhost:8765/billing/hi
I get Hello Khushboo message.
But when I stop the BillingService microservice and again send the same request, the circuit breaker method doesn't get invoked.
Also, while accessing the Actuator Health status, I do not see circuit breaker information in the status logs which I should see.
I even added the CircuitBreaker code in OrderService which actually calls the BillingService:
#CircuitBreaker(name="BillingServiceCapture", fallbackMethod = "getAllBillingDetails")
public TransactionResponse saveOrder(TransactionRequest request) {
Order order=request.getOrder();
Billing billing=request.getBilling();
billing.setOrderId(order.getId());
billing.setAmount(order.getPrice());
Order ordermade=orderRepo.save(order);
Billing billingresponse=billingproxy.getBillingDone(billing);
TransactionResponse response=null;
String responseStr= billingresponse.getPaymentStatus().equals("success")?"Payment processing successful":"Payment failed";
response=new TransactionResponse(order, billingresponse.getTransactionId(),billingresponse.getAmount(),responseStr);
return response;
}
public Billing getAllBillingDetails(Billing bill,Exception e) {
return new Billing(1000,"pass",101,102,1000);
}
When I call http://localhost:8765/order/bookorder - this throws a 500 internal server exception but CircuitBreaker is not called. The error is:
[503] during [GET] to [http://billing-service/billing/preparebill] [BillingProxy#getBillingDone(Billing)]: [Load balancer does not contain an instance for the service billing-service]
Please note for testing purpose I'm not starting BillingService so the instance is not available for OrderService Feign to call.
Any insights will be appreciated.
Thanks.
The fallback method should pass the Exception parameter and return the same type as the original method:
public String hiFallback(Exception e) {
return "Hello Khushboo FallBack!";
}

How to pass and handle Exceptions through HTTP responses in Spring?

I have a Client and Server module in my Spring project running on separate ports. The Client module makes a POST request to the Server via a RestTemplate. The Server-Module throws a custom Exception with a custom error-message. Currently, in my Project, the Server has a RestControllerAdvice Class that handles such exceptions as follows:
#RestControllerAdvice
public class AppRestControllerAdvice {
#ExceptionHandler(ApiException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public MessageData handle(ApiException e) {
MessageData data = new MessageData();
data.setMessage(e.getMessage());
return data;
}
}
On the Client side, the following method catches the Response from the Server.
#RestControllerAdvice
public class AppRestControllerAdvice {
#ExceptionHandler(ApiException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public MessageData handle(ApiException e) {
MessageData data = new MessageData();
data.setMessage(e.getMessage());
return data;
}
#ExceptionHandler(Throwable.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public MessageData handle(Throwable e) {
MessageData data = new MessageData();
data.setMessage("UNKNOWN ERROR- " + e.getMessage());
e.printStackTrace();
return data;
}
}
Whenever the Exception is thrown on the server, here is what I receive on the Client
{
"message": "UNKNOWN ERROR- org.springframework.web.client.HttpClientErrorException: 400 Bad Request"
}
My question is, how do I retrieve the Custom Exception message that originated on the Server?
Also, why isn't the correct RestControllerAdvice module on the Client side picking up the error? (The INTERNAL_SERVER_ERROR method catches the error instead of the BAD_REQUEST method.)
My question is, how do I retrieve the Custom Exception message that originated on the Server?
To retrieve the orignal exception message you have to use dedicated ResponseErrorHandler that is capable of extracting that information, rather than using the default one (DefaultResponseErrorHandler - which I assume you use because of the message you got - org.springframework.web.client.HttpClientErrorException: 400 Bad Request).
Create:
public class CustomerResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse httpResponse) throws IOException {
// here you have access to the response's body which potentially contains the exception message you are interested in
// simply extract it if possible and throw an exception with that message
// in other case you can simply call `super.handlerError()` - do whatever suits you
}
}
Then use it with your RestTemplate:
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
          .errorHandler(new CustomerResponseErrorHandler())
          .build();
}
}
Also, why isn't the correct RestControllerAdvice module on the Client side picking up the error? (The INTERNAL_SERVER_ERROR method catches the error instead of the BAD_REQUEST method.)
The correct method is executed - your RestTemplate at the moment is throwing HttpClientErrorException which is not an ApiException. It is a Throwable though.

Spring Feign client call enters exception block when it should stay in try block

Need some small help about Spring Feign client. So here is the situation,
I have 2 Spring boot services. Let’s say Service A and Service B. I have configured my Service A with Feign client through which I call the Service B method.
So here is the code for my Service A,
My FeignCleint config interface,
#FeignClient(name = "FeignClient", url = "http://localhost:8081/ServiceB/hello")
public interface FeignApi {
#RequestMapping(method = RequestMethod.GET)
ResponseEntity<?> hello();
}
And my rest controller that uses above feign config to call the Service B method,
#RestController
public class ApiController {
#Autowired
private FeignApi feignApi;
#RequestMapping(value = "/callServiceB")
public ResponseEntity<?> companyInfo() {
ResponseEntity<?> response = new ResponseEntity("OK Response", HttpStatus.OK);
try {
response = feignApi.hello();
// Code for some other things related to application.
return response;
} catch (Exception ex) {
System.out.println("Service A Exception block reached.");
return new ResponseEntity(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
}
below is my controller for Service B,
#RestController
public class MyController {
#GetMapping("/hello")
public String hello() throws Exception {
if (true) {
throw new Exception("Service B Exception...");
}
return "Hello World";
}
}
And my Controller advice to handle the exception that I am manually throwing,
#ControllerAdvice
public class MyControllerAdvice {
#ExceptionHandler
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<?> handleException(Exception exception, Model model) {
return new ResponseEntity<>("Caused due to : " + exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Now my flow is like below,
As you can see, I am calling service B from service A using feign client. My service B is throwing an exception manually which I am catching using the controller advice and sending the exception details as an ResponseEntity back to the calling service A. So that Service A can process the details and move forward based on that.
The problem is when I hit the call from Service A using
http://localhost:8080/feign/callServiceB
The service B fails as expected. Now what I expect is that the Service A should receive the response back in form of the ResponseEntity. But what really happens is that the flow enters the exception block instead of staying in the try block. I can see this line printed,
"Service A Exception block reached."
This is what I don't understand. If I have managed the service B exception using controller advice and sent back the response to service A in form of ResponseEntity, then how come the flow of service A enters catch block. I expect it to stay in try block only as I want to process further based on the data.
Any idea, how can I get around this thing? Or is this how it will behave even when I am using controller advice to manage exceptions? What should be the expected behavior in this case?
Please advice.
By default Feign throws FeignException for any error situation.
Make use of fallback mechanism to handle failures.
#FeignClient(name = "FeignClient", url = "http://localhost:8081/ServiceB/hello", fallback= FeignApiFallback.class)
public interface FeignApi {
#RequestMapping(method = RequestMethod.GET)
ResponseEntity<?> hello();
}
#Component
class FeignApiFallback implements FeignApi {
#Override
public ResponseEntity<?> hello() {
//do more logic here
return ResponseEntity.ok().build();
}
}
make sure you add below property to wrap methods in hystrix commands in recent releases
feign.hystrix.enabled=true
Any status other than 200, feign client will consider it as an exception and you are setting HttpStatus.INTERNAL_SERVER_ERROR in your controller advice.
You can use custom ErrorDecoder
refer https://github.com/OpenFeign/feign/wiki/Custom-error-handling

How to handle Exception occuring when returning StreamingResponseBody from RestController

I have implemented a Spring Rest Controller that streams back large files using the StreamingResponseBody. However, these files are coming from another system and there is the potential for something to go wrong while streaming them back. When this occurs I am throwing a custom Exception (MyException). I am handling the exception in an #ExceptionHandler implementation which is below. I am attempting to set the response httpstatus and error message but I am always receiving http status 406. What is the proper way to handle errors/exceptions while returning a StreamingResponseBody?
#ExceptionHandler(MyException.class)
public void handleParsException( MyException exception, HttpServletResponse response) throws IOException
{
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),exception.getMessage());
}
You should handle all errors in the same way. There are many options.
I prefer next:
Controller Advice
It is a good idea to have an entity to send a generic error response, an example:
public class Error {
private String code;
private int status;
private String message;
// Getters and Setters
}
Otherwise, to handle exceptions you should create a class annotated with #ControllerAdvice and then create methods annotated with #ExceptionHandler and the exception or exceptions (it could be more than one) you want to handle. Finally return ResponseEntity<Error> with the status code you want.
public class Hanlder{
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleResourceNotFoundException(MyException
myException, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.CONFLICT.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.CONFLICT);
}
#ExceptionHandler({DataAccessException.class, , OtherException.class})
public ResponseEntity<?> handleResourceNotFoundException(Exception
exception, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.INTERNAL_ERROR.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.INTERNAL_ERROR);
}
}
Other ways:
Annotate exception directly
Other way is annotating directly the excetion with the status and the reason to return:
#ResponseStatus(value=HttpStatus.CONFLICT, reason="Error with StreamingResponseBody")
public class MyError extends RuntimeException {
// Impl ...
}
Exception Handler in a specific controller
Use a method annotated with #ExceptionHandler in a method of a #Controller to handle #RequestMapping exceptions:
#ResponseStatus(value=HttpStatus.CONFLICT,
reason="Error with StreamingResponse Body")
#ExceptionHandler(MyError.class)
public void entitiyExists() {
}
I figured the problem out. The client was only accepting the file type as an acceptable response. Therefore, when returning an error in the form of an html page I was getting httpstatus 406. I just needed to tell the client to accept html as well to display the message.

Server sends INTERNAL_SERVER_ERROR, but client receives OK

I've got a method on the server which returns HttpStatus.INTERNAL_SERVER_ERROR but the client application always gets OK http status.
This is servers method (It's implified):
#RequestMapping(value="/example", method = RequestMethod.POST)
HttpStatus createSomething(Principal principal, #RequestBody #Valid SomeObject so) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
I'm sure that the right request is being made by cient application. Any ideas what migh cause the problem?
It won't work like that you should either:
return new ResponseEntity<String>(HttpStatus. INTERNAL_SERVER_ERROR)
or throw some custom exception with http status:
ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalServerException extends RuntimeException {
public InternalServerException (String message) {
super(message);
}
}

Resources