I need to invoke a rest service asynchronously and I thought of using spring reactive's webclient instead of the AsyncRestTemplate. However my url is not getting invoked at all with the below code.
Mono<Test> asyncResponse = webClientBuilder.build().post().uri(url).contentType(MediaType.APPLICATION_JSON)
.header("h1", h1).header("h2", h2)
.body(BodyInserters.fromObject(request))
.retrieve().bodyToMono(Test.class);
However if I do the same synchronously everything works fine.
webClientBuilder.build().post().uri(url).contentType(MediaType.APPLICATION_JSON)
.header("h1", h1).header("h2", h2)
.body(BodyInserters.fromObject(request))
.exchange();`
What am I doing wrong?
exchange doesn't mean synchronous. It responds Mono. You need to subscribe() or block() your stream somewhere.
Difference with exchange and retrieve is : They differ in return types; the exchange method provides a ClientResponse along with its status, headers while the retrieve method is the shortest path to fetching a body directly. you can refer this
Related
I want to use WebClient response in the next API call. So before calling the next api some of the fields from the response extract and use them in the next api call. There is a way to block WebClient response and use it. But is there any way to do it without blocking? So my code looks like this
response = getUserByWebClient1(); // web client call 1
extract id from response
getRolesByUserId(id); // webclient call 2
This is not specific to WebClient, but a general concept with reactive types, here Reactor Flux and Mono.
You can use the flatMap operator to achieve just that.
// given UserService with a method "Mono<User> getCurrentUser()"
// and RolesService with a method "Mono<RoleDetails> getRolesForUser(Long userId)"
Mono<RolesDetails> roles = userService.getCurrentUser()
.flatMap(user -> rolesService.getRolesForUser(user.getId());
I've created a synthetic application using Spring Reactive framework to investigate caching mechanics, that is proposed with Webflux.
What I've noticed, is that when I use a Webclient, that addresses to a third party URL, a method that uses it is called twice, whereas a WebClient, that addresses to my own endpoint is called only one time per request as expected.
I wonder why is it so?
Here is my code for page abstraction, when a webClient is associated with localhost URL, a method getBody() is called only once per request. But when webClient is associated with https://other.size, this method is invoked twice so I see log.info messages two times:
public class Page {
private Mono<String> res;
public Page(WebClient webClient, String url) {
res = webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.cache();
}
public Mono<String> getBody() {
log.info("getting data");
return res;
}
}
Here is a link to the full project: https://github.com/RassulYunussov/webluxmistery
Thanks for the video. It was really helpful.
So if you hit the /tengri endpoint from the browser, you will receive the logs twice and I can confirm I see the same behaviour on my machine.
However, if you hit /tengri using curl you only get the log line once.
Furthermore, looking into the network traffic on the browser I can see a 2nd api call being made to the /tengri endpoint.
Is there some additional logic that will happen when the html is rendered by the browser that will make a 2nd call to /tengri?
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 trying to invoke a AWS Lambda function asynchronous from the AWS API Gateway.
I have a long running (2-3min) Lambda function and I want to invoke this Lambda function asynchronous from a HTTP Post request. I configured the API Gateway as a Lambda Proxy Integration (because I want to pass the body unmodified to the function) This is working fine, but after 30s I get a 504 due the API Gateway execution time restriction.
But I can't manage to call the function async. According to the AWS docs it should be possible if I set the haeder "X-Amz-Invocation-Type", but this doesn't make any difference.
Does anybody know if it is possible to invoke a function async and using the proxy integration?
AWS says it's possible if you set the X-Amz-Invocation-Type header to Event, but I ran into the same necessity a few months ago and this did not work for me, so I am not sure this is still the case or if it was just me who misconfigured it. Maybe you are missing the same thing as me back then: I did not add an InvocationType header on the Integration Request as the docs suggests, so this very likely is the case for you, but still, I can't guarantee it works)
The documentation says:
Configure Lambda asynchronous invocation in the API Gateway console
In Integration Request, add an X-Amz-Invocation-Type header.
In Method Request, add an InvocationType header and map it to the
X-Amz-Invocation-Type header in the Integration Request with either a
static value of 'Event' or the header mapping expression of
method.request.header.InvocationType. For the latter, the client must
include the InvocationType:Event header when making a request to the
API method.
If this works, then you are good to go.
What I did back then, however, was to create an intermediate Lambda which literally acted as proxy to the actual Lambda.
There are a wide range of options to execute your function asynchronously, but you will need two Lambda functions regardless.
One option is to invoke another function (which will actually execute the task you want) asynchronously via the function invoked by API Gateway.
const params = {
FunctionName: 'YOUR_FUNCTIONS_NAME',
InvocationType: 'Event',
Payload: JSON.parse(event.body) // this is the event coming from API Gateway
};
await lambda.invoke(params).promise(); // await here is only going to wait for the HTTP request to be successful. Once the 2nd Lambda is invoked, it will return immediately
Another option is to put a message in SQS and configure a trigger for your Lambda to be invoked when there's a new message in the SQS queue. Same thing applies for a SNS notification.
Other options include Kinesis, DynamoDB Streams, etc. but the idea is the same: the function invoked via API Gateway must be nothing but a proxy to the other Lambda. How this proxy is going to work (be it sending a message to SQS, SNS, invoking the other function asynchronously directly, etc.) does not matter, what matters is the concept to get around API Gateway's 30 seconds request limit.
I am using Spring 4.3 and SpringBoot 1.5 to create a component that has to call an external REST service. This external service requests HTTP Basic Authentication.
I found that SpringBoot provides a very useful builder to set up RestTemplate properties, such as basic authentication, RestTemplateBuilder.
To call the external REST service I have to use the method RestTemplate.exchange, because of the List<T> return type.
new RestTemplateBuilder().basicAuthorization(username, password)
.build()
.exchange("/some/path/with/variables",
HttpMethod.GET,
new HttpEntity<>(new HttpHeaders()),
new ParameterizedTypeReference<List<Integer>>() {},
some, params)
The problem here is that the signature of the exchange method requests an object of type HttpEntity, that is basically a container of HttpHeaders.
The question is, will the HttpEntity object, passed as input parameter to the exchange method, subscribe the Basic Auth header set using the RestTemplateBuilder.basicAuthorization method?
Thanks a lot.
I have done some experiments during the past couple of days. The instance of HttpEntity passed to the exchange method does not subscribe the information relative to the Basic Authentication set by the RestTemplateBuilder. The two sets of headers are merged into one.
Nice shot, Spring ;)