I am using Resttemplate to build an simple API gateway in spring-boot project. When my gateway receive a request from client, it dispatch the request to another Service through RESTful call, and then pass the response back to client.
My code snippet like below:
#RestController
#RequestMapping("api/v1/message")
public class GatewayController {
#PostMapping
public ResponseEntity<Response> dispatchRequest(#RequestBody Request request) {
validateInput(request);
Response response = restTemplate.post(URL_OF_ANOTHOER_SERVICE, request, ...);
return ResponseEntity.ok(response);
}
}
I have more than 100 requests per second approximately, and I know that Resttemplate is thread-safe.
My questions:
Is Resttemplate OK to do such work? Will it become bottleneck?
Is there any other suggestions?
Thank you very much.
You can try Spring Cloud Gateway or Spring Cloud Netflix.
You don`t need to build a gateway from scratch unless you want. And even if you want to, you could consider aspects such as security, maturity and maintenance
Some modules of Spring Cloud Netflix
have gone into maintenance mode, therefore, Spring Cloud Gateway might be a good option as GJohannes quoted
Related
My backend is a webflux server using the graphql-java-kickstart (v11.0.0) library and I have a bunch of graphql http requests right now.
I'm using Spring's WebTestClient to test my graphql Queries and Mutations.
Basically, I'll just call it and compare the response like so
webTestClient
.post()
.uri("/graphql")
.header(...)
.body(Mono.just(request), JsonNode.class)
.exchange()
.expectStatus()
.isOk()
.expectBody(JsonNode.class)
.returnResult()
.getResponseBody()
But this is over a normal HTTP and simple REST protocol.
With GraphQL Subscriptions, I need to set up a websocket and do the Apollo (subscription-transport.ws) handshake protocol before sending the Subscription request in, and I have to wait for a response async.
I have manually verified the subscription works with the WebSocket Test Client chrome extension
What's the correct way to write a unit test for this?
Suppose I have a simple Subscription that just returns the timestamp back every second
type Subscription {
clock: String!
}
How do I write a unit test for this? What library or client should I use?
A link or example would be awesome!
I have an existing spring-mvc application with different #RestController. Now I want to add a Mono<String> endpoint, and log the request timestamp with url path, as well as the response timestamp.
But how? I cannot simply #EnableWebFlux as I would have to disable spring-mvc therefor.
How could I register a filter explicit for the webflux endpoint that catches on invocation, and right before the response is written back async?
#RestController
public class FluxService {
#PostMapping
public Mono<String> post() {
return webClient.post().uri(uri).bodyValue(payload).retrieve().bodyToMono(String.class);
}
}
I want enhanced logging on the #PostMapping endpoint, not on the webClient!
MVC is a blocking/servlet based api, mixing it with webflux isn't going to work. They use different filters, security etc as MVC assumes a lot of ThreadLocal stuff. (Can I use SpringMvc and webflux together?)
If you really want to mix them like this you could use .doOnNext() once you get your mono and call your logger there.
I am confused about how an infinite loop of feign calls might behave.
An example:
Assume I have 2 APIs, A & B.
if I call API A, which in turn calls API B via a feign HTTP call, which in turn calls API A again via feign, will it recognize this and break the call chain?
Quick flowchart of calls:
A -> B -> A -> B ... Repeat infinitely?
I have not tried this code, it is just an idea。
But I am assuming that spring-cloud-starter-feign will provide some methods to resolve this problem? Is this assumption correct?
#PostMapping(RestJsonPath.API_A)
ResponseEntity<byte[]> apiA();
#PostMapping(RestJsonPath.API_B)
ResponseEntity<byte[]> apiB();
Will it execute until it times out or hystrix will stop it?
TL;DR:
Feign will keep the connection open on the initial request from A to B until the pre-configured timeout kicks in. At this point, Feign will time out the request and if you have specified a Hystrix fallback, Spring will use your Hystrix fallback as the response.
Explanation:
spring-boot-starter-feign provides an abstraction layer for writing the HTTP request code. It will not handle potential loops or cycles in your code.
Here is an example spring boot feign client from their tutorials website for demonstration:
#FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
configuration = ClientConfiguration.class,
fallback = JSONPlaceHolderFallback.class)
public interface JSONPlaceHolderClient {
#RequestMapping(method = RequestMethod.GET, value = "/posts")
List<Post> getPosts();
#RequestMapping(method = RequestMethod.GET, value = "/posts/{postId}", produces = "application/json")
Post getPostById(#PathVariable("postId") Long postId);
}
Notice first that this is an interface - all the code is auto generated by Spring at startup time, and that code will make RESTful requests to the urls configured via the annotations. For instance, the 2nd request allows us to pass in a path variable, which Spring will ensure makes it on the URL path of the outbound request.
The important thing to stress here is that this interface is only responsible for the HTTP calls, not any potential loops. Logic using this interface (which I can inject to any other Spring Bean as I would any other Spring Bean), is up to you the developer.
Github repo where this example came from.
Spring Boot Docs on spring-boot-starter-openfeign.
Hope this helps you understand the purpose of the openfeign project, and helps you understand that it's up to you to deal with cycles and infinite loops in your application code.
As for Hystrix, that framework comes in to play (if it is enabled) only if one of these generated HTTP requests fails, whether it's a timeout, 4xx error, 5xx error, or a response deserialization error. You configure Hystrix, as a sensible default or fallback for when the HTTP request fails.
This is a decent tutorial on Hystrix.
Some points to call out is that a Hystrix fallback must implement your Feign client interface, and you must specify this class as your Hysterix fallback in the #FeignClient annotation. Spring and Hystrix will call your Hystrix class automatically if a Feign request fails.
I'm developing some reactive microservices using Spring Boot 2 and Spring 5 with WebFlux reactive starter.
I'm facing the following problem: I want to handle all HTTP Statuses that I receive from calling another REST Services and throws an exception when I receive some bad HTTP Status. For example, when I call an endpoint and I receive an 404 HTTP Status, I want to throw an exception and that exception to be handled in some ExceptionHandler class, just like the way it was in Spring 4 with #ControllerAdvice.
What is the right way to do this? Hope to receive some good suggestions.
This can be addressed in two independent parts.
How to convert HTTP 404 responses received by WebClient into custom exceptions
When using WebClient, you can receive HTTP 404 responses from remote services. By default, all 4xx and 5xx client responses will be turned into WebClientResponseException. So you can directly handle those exceptions in your WebFlux app.
If you'd like to turn only 404 responses into custom exceptions, you can do the following:
WebClient webClient = //...
webClient.get().uri("/persons/1")
.retrieve()
.onStatus(httpStatus -> HttpStatus.NOT_FOUND.equals(httpStatus),
clientResponse -> Mono.error(new MyCustomException()))
.bodyToMono(...);
This is obviously done on a per client call basis.
You can achieve the same in a more reusable way with an ExchangeFilterFunction that you can set once and for all on a WebClient instance like this:
WebClient.builder().filter(myExchangeFilterFunction)...
How to handle custom exceptions in WebFlux apps
With Spring WebFlux with annotations, you can handle exceptions with methods annotated with #ExceptionHandler (see Spring Framework reference documentation).
Note: using a WebExceptionHandler is possible, but it's quite low level as you'll have no high-level support there: you'll need to manually write the response with buffers without any support for serialization.
I think what you are looking for is WebFluxResponseStatusExceptionHandler the check this for reference.
In the WebHandler API, a WebExceptionHandler can be used to to handle
exceptions from the chain of WebFilter's and the target WebHandler.
When using the WebFlux Config, registering a WebExceptionHandler is as
simple as declaring it as a Spring bean, and optionally expressing
precedence via #Order on the bean declaration or by implementing
Ordered.
This example may help, have not tried it myself.
#Component
#Order(-2)
class RestWebExceptionHandler implements WebExceptionHandler{
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof PostNotFoundException) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
// marks the response as complete and forbids writing to it
return exchange.getResponse().setComplete();
}
return Mono.error(ex);
}
}
class PostNotFoundException extends RuntimeException {
PostNotFoundException(String id) {
super("Post:" + id + " is not found.");
}
}
I looked for the similar question but could not find any . I have a micro service created with drop-wizard which is running in localhost:9000.
I am working in another project(with spring mvc) running in 8080. I wan to call the above service which gives me string back from any of my controllers in main project .lets say the path is "localhost:9000/giveMeString" .
You can use Apache's HTTP Client. See this example borrowed from their documentation:
// Create an instance of HttpClient.
HttpClient client = new HttpClient();
// Create a method instance.
GetMethod method = new GetMethod("http://localhost:9000/giveMeString");
// Execute the method.
int statusCode = client.executeMethod(method);
// Read the response body.
byte[] responseBody = method.getResponseBody();
//Print the response
System.out.println(new String(responseBody));
If you're really going down the microservice path, note that creating an HTTP client for every request and making synchronous, blocking requests won't really scale.
Here are a few ideas, if you're using Spring:
Create a single RestTemplate instance
You can create a single RestTemplate instance and inject it in multiple places in your application.
#Configuration
public class MyConfiguration {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate(new HttpComponentsClientHttpRequestFactory());
}
}
Better, wrap that REST client with a Cache
Check out Sagan's take on this.
Even better, go asynchronous
You can use an AsyncRestTemplate; very useful, especially if your Controllers need to make multiple requests and if you don't want to block your webapp thread. Your Controller can even return a DeferredResult or a ListenableFuture, which will make your webapp more scalable.
And Spring Cloud + Netflix
You can also check Spring Cloud and Spring Cloud Netflix.
You'll see there interesting features: load-balancing, recovery, circuit breakers, etc.