In my current project we are using aws-lambda to make a rest call to external service and consume the response. Happy path works fine but when it comes to connection-timeout or socket-timeout it is not working as expected. Little more details below
When making a call to external system and if the read-timeout scenario happens (external system connection got established but did not receive any response from the system within 15 sec) the aws lambda keeps waiting for the response till lambda-timeout (25 sec) and returns error.
But I expect the rest-call code invoked within lamda to throw the SocketTimeOutException or related one which is not happening.
Same thing, when I tried using a sample java code (using apache's http-client implementation which is what I have used in lambda) it works perfectly fine and I am getting proper exception thrown.
Initially we tried with jersey implementation for making rest-call and thought this is having issue and changed to http-client implementation, but none of them thrown the exception as it does in sample java code.
Please let me know your suggestions or solutions if faced already.
Below is the code snippet that I use in both lambda as well as sample program for making the rest call. (this whole block is wrapped under try-catch)
HttpPost post = new HttpPost(URL);
RequestJSONObject request = new RequestJSONObject();
//setting required request payload
ObjectMapper mapper = new ObjectMapper();
String jsonStr = mapper.writeValueAsString(request);
post.setEntity(new StringEntity(jsonStr));
post.addHeader("content-type", "application/json");
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(1000)
.setSocketTimeout(3000).build();
CloseableHttpClient httpClient =
HttpClientBuilder.create().setDefaultRequestConfig(config).build();
CloseableHttpResponse response = httpClient.execute(post);
Thanks,
Ganesh Karthik C.
Related
I have a Web Api that invokes another web api call to get some information. In order to make the app more resilient, I implemented a HttpTransientErrorDetectionStrategy following the steps at: https://alexandrebrisebois.wordpress.com/2013/02/21/defining-an-http-transient-error-detection-strategy-for-rest-calls/
After that, I use code like below to invoke the other web app:
RetryPolicy _retryPolicy = new RetryPolicy<HttpTransientErrorDetectionStrategy>(
new ExponentialBackoff(retryCount: 2, minBackoff: TimeSpan.FromSeconds(0), maxBackoff: TimeSpan.FromSeconds(10), deltaBackoff: TimeSpan.FromSeconds(2)));
var _httpClient = new HttpClient
{
BaseAddress = new Uri("http://www.microsoft.com")
};
HttpResponseMessage response = _retryPolicy.ExecuteAsync(async () => await _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead)).Result;
The _httpClient.GetAsync call gets stuck, and I have no idea why. If I remove the _retryPolicy, and just use _httpClient.GetAsync directly, it returns in a matter of seconds.
I have similar code on a console app, to invoke the same web app, and that is working fine, so this seems to be specific to the way I am using it in my web API. This is intended to be an app on Azure, but it happens when I debug locally as well. Does anybody have any idea why this is getting stuck? How can I debug this?
Thank you!
I have similar code on a console app, to invoke the same web app, and that is working fine, so this seems to be specific to the way I am using it in my web API.
The code you posted is blocking right here:
HttpResponseMessage response = _retryPolicy.ExecuteAsync(...).Result;
Don't block on async code. Instead, use await:
HttpResponseMessage response = await _retryPolicy.ExecuteAsync(...);
If I remove the _retryPolicy, and just use _httpClient.GetAsync directly, it returns in a matter of seconds.
If your original code is blocking, and you must block on asynchronous code (for some reason), then you can either use the ConfigureAwait(false) hack:
HttpResponseMessage response = _retryPolicy.ExecuteAsync(async () => await _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead).ConfigureAwait(false)).Result;
or elide async/await:
HttpResponseMessage response = _retryPolicy.ExecuteAsync(() => _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead)).Result;
P.S. Check out DecorrelatedJitterBackoffV2.
I am using Hangfire to execute recurring jobs in my web API and I use System.Web.HttpContext.Current.Server.MapPath in the "RoutineMethod" function.
But the problem is it throws object null exception.
I searched the problem and found that recurring jobs don't use http threads, they use background threads.
Now to resolve this problem I need to call my internal (endpoint) using httpclient.
But to do that I need to give URL of the Web API (to generate URI). So is there any other way to call internal function using httpclient.
My current code:
public static async Task<IHttpActionResult> RoutineTask()
{
//It was like this before.
//await new DemoController().RoutineMethod();
//await new DemoController().RoutineMethod2();
//I am planning to do this.
using (var client = new HttpClient())
{
//But I need to give URI which I don't think is a good idea.
var uri = new Uri("http://localhost/DemoApp/api/DemoController/RoutineMethod");
await client.GetAsync(uri);
}
return new DemoController().Ok();
}
The short answer is no. The HttpClient, as its name implies, requires an http connection.
There are no smell issues with storing service connection information in a configuration file.
However, to expand on Nkosi's comment, it appears that since your code can create an instance of DemoController, that it must have a reference to that controller's project or even be in the same project. I would extract the interesting code into a library or service that all areas needing the information can reference.
I've been working on a .NET 4.6.1 Web API project. As part of this project, I need to call another Web API and I want to use the HttpClient to do so.
From my research online, you can't rely on just doing a normal HttpClient within a using clause as it doesn't garbage collect correctly and can lead to memory leaks.
E.g., I'm currently using it as follows:
using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds(CONTENTFUL_TIMEOUT_IN_SECONDS) } )
{
responseText = await client.GetStringAsync(uri).ConfigureAwait(continueOnCapturedContext:false);
}
But as suggested in other articles from Stack Overflow and others, this leads to memory leaks, and the way around this is to share a single instance of the HttpClient.
E.g., check HTTPCLIENT DESTABILIZING YOUR SOFTWARE and HttpClientHandler/HttpClient Memory Leak.
I'm not sure however how to setup a shared "single" instance of the HttpClient from within an WebAPI itself?
You should have a look on how to implement singleton pattern. Refer to this.
Then you can create a singleton of HttpClient and make it responsible for all HTTP calls from your API.
I wanted to analyze the improvement I may see by enabling Async Controllers in Spring Boot over normal controller
So here is my test code. One API returns a Callable and another is normal controller API. Both APIs block for 10secs simulating a long running task
#RequestMapping(value="/api/1",method=RequestMethod.GET)
public List<String> questions() throws InterruptedException{
Thread.sleep(10000);
return Arrays.asList("Question1","Question2");
}
#RequestMapping(value="/api/2",method=RequestMethod.GET)
public Callable<List<String>> questionsAsync(){
return () -> {
Thread.sleep(10000);
return Arrays.asList("Question2","Question2");
};
}
I set up embedded tomcat with this configuration i.e only one tomcat processing thread:
server.tomcat.max-threads=1
logging.level.org.springframework=debug
Expectations for /api/1
Since there is only one tomcat thread, another request will not be entertained untill this is processed after 10secs
Results:
Meet expectations
Expectations for /api/2
Since we are returning a callable immediately, the single tomcat thread should get free to process another request. Callable would internally start a new thread. So if you hit the same api it should also gets accepted.
Results:
This is not happening and untill the callable executes completely, no further request is entertained.
Question
Why is /api/2 not behaving as expected?
#DaveSyer is right, /api/2 is actually behaving as expected.
I assume you are testing the behavior with a web browser. At least Firefox and Chrome are preventing multiple simultaneous requests to the same URL. If you open 2 tabs with api/2, the second one will only send a request to the application after the first got the response.
Try testing it with a simple bash script, like:
curl localhost/api/2 &
curl localhost/api/2 &
curl localhost/api/2 &
It will print 3 responses around the same time.
Just want to mention that server.tomcat.max-threads is deprecated since Spring boot 2.3. Now use server.tomcat.threads.max in your Spring application.properties. The default is 200.
I use restlet client to send rest request to the server.
public class RestHandler {
protected ClientResource resource = null;
protected Client client = null;
public void connect(final String address,
final Protocol protocol){
final Context context = new Context();
if (client == null) {
logger.info("Create Client.");
client = new Client(context, protocol);
}
resource = new ClientResource(context, new Reference(protocol, address));
resource.setNext(client);
resource.setEntityBuffering(true);
}
}
In its child class, use resource.get()/post/put/delete to send rest request.
I found the response come back so slow at the first time(5-10s).
And then it go faster in the next few requests.
But after waiting about 10min I send the request again, it become slow again.
Is there any way to make the response come back faster?
You can try to use another client connector. It can be the cause of your problem, especially if you use the default one. Notice that the default one should be used for development only.
This page gives you all the available client connectors: http://restlet.com/technical-resources/restlet-framework/guide/2.3/core/base/connectors.
Regarding client connectors, you can configure properties to tune them. To use a client connector, simply put the corresponding Restlet extension within your classpath. Perhaps can you make a try with the extension org.restlet.ext.httpclient.
This answer could help you regarding connector configuration and properties: Restlet HTTP Connection Pool.
Hope it helps you,
Thierry