Async Spring Controllers vs normal Controllers - spring

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.

Related

SocketTimeOutException not thrown when making a rest-call inside AWS lamda (java)

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.

Calling internal (Endpoint) function in WebAPI

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.

Spring Boot - Timed Controller

I have the following Spring Boot Web Application and used Tomcat as a server and Thymeleaf for my html templates. I have two questions regarding my first Spring Boot project.
This is how my code looks like so far:
#RequestMapping(value = "/myIpAdress")
private String displayMyIpAdress(HttpServletRequest request, Model model) throws Exception
{
model.addAttribute("myIpAdress", getMyIpAdress());
return "myIpAdress";
}
private String getMyIpAdress()
{
// here is a simple HTTP Request returning my IP Adress from a server
}
This is the myIpAdress.html
<div th:text="${myIpAdress}"></div>
All it does it displaying my IP Adress on my view, works great. Nothing fancy so far.
First question:
How do I set up a timer, to display my IP every second on my html File? I want an IMMEDIATE change in my View as soon as my IP changes. In Java I would simply do a scheduled timer or code a while(true) loop with a Thread.sleep(1000L) like this:
public static void main(String[] args) throws Exception
{
while(true){
System.out.println("My IP is: "+getMyIpAdress());
Thread.sleep(1000L);
}
}
This Java code works VERY fast. The console displays my IP adress almost every second. Which is exactly what I wanted initially. But now I wanted to make a Spring Boot Web App out of it and it is incredibly slow and the request takes up to 15 seconds to make the request and render the view.
So my second question is, why is it that slow? And how can I improve the speed? I don't have any CSS or JavaScript running on the HTML file, it really only displays my IP adress.
All I want is to fetch the IP from the server and display it on my view. Every second. How do I achive this?
In pure Thymeleaf you can't update the static view every time something changes in your backend without sending a new request from the client side. It works one way only - requests are from the client to the server, not the other way around.
The easiest way to achieve what you want to do is to add a simple javascript to the template which fetches the IP address every second (eg. from a special dedicated endpoint) and updates the view.
Or you could add some websocket support to the template, but I don't think it's needed in your case (seems to be an overengineering)...

MiniProfiler with Web.API 2; is there a global magic request context object?

I'm trying to setup MiniProfiler on my web api site, and having a hard time getting MiniProfiler.Current to work.
I followed the directions at miniprofiler.com, and have the following in global.asax:
protected void Application_Start()
{
MiniProfilerEF6.Initialize();
// other setup
}
protected void Application_BeginRequest() {
// need to start one here in order to render out the UI
MiniProfiler.Start();
}
protected void Application_EndRequest() {
MiniProfiler.Stop();
}
This uses the default WebRequestProfilerProvider, which stores the actual profile object in HttpContext.Current.Items.
When I ask for MiniProfiler.Current, it looks to HttpContext.Current.
When I make a request for one of my web api URLs:
Application_BeginRequest creates the profiler, store it in HttpContext.Current
in a web api MessageHandler, I can see HttpContext.Current
in a web apu IActionFilter, HttpContext.Current is now null, and my attempt to MiniProfiler.Current.Step("controller:action") fails
my EF queries run from various services do not get recorded, as that miniprofiler hook relies MiniProfiler.Current, which relies on HttpContext.Current, which is null right now
Application_EndRequest fires, and HttpContext.Current is magically back, and so it wraps up the profiler and tells me how long it's been since the request began
I dug through the code, and I can create my own IProfileProvider, to store the profiler object somewhere more reliable than HttpContext.Current, but I don't know where that could be.
I spent a few hours trying things out, but couldn't find a workable solution. The problems:
the IProfileProvider is a global variable; all worker threads in either the MVC or Web API pipeline all have to use the same IProfileProvider
I can dig around in web api RequestContext.Properties to pull out the HttpContext for that request, but that doesn't really help because my IProfileProvider is global across the entire app; If I tell it to store the profile in HttpContext A, then any simultaneous requests for other HttpContexts are going to pollute the profile
I can't rely on any kind of threadstorage because of async/await re-using threads dynamically
I can't stick the profiler object in an Ninject binding with InRequestScope because InRequestScope doesn't seem to work with web api 2.1, but even if I could
everyone says HttpRequestMessage.Properties is the new HttpContext.Current.Items, but again, IProfileProvider is a global variable and I don't know of a way to ensure each request is looking at their version HttpRequestMessage. MiniProfiler.Current can be called from anywhere, so I guess a global IProfileProvider would have to somehow inspect the call stack to and find an HttpRequestMessage there? That sounds like madness.
I'm at a loss. What I really want is a special variable.
The process of putting the question together I figured it out. HttpContext.Current can get lost when you async/await things: Why is HttpContext.Current null after await?
I had to make the web.config change listed there, and adjusted my filters to use Miniprofiler.Current before any awaiting.
Also discussed at https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/

Websocket message interupts dynamic loading of ExtJS class

in my ExtJS 4.1 application I use a websocket connection to remotely insantiate and control ExtJS classes from the server. The client is registered to websocket.onmessage and is waiting for incoming commands.
I defined a simple protocol for that. The server sends a "CREATE classname id". On client side I use Ext.create to instantiate the class. The server then can send commands via the websocket to the object. E.g. "DOSTUFF id". I'm using the dynamic loading mechnism of ExtJS.
In Chrome everything works fine.
The problem with Firefox is, that the second command message (DOSTUFF) is executed BEFORE the object has been created. This leads to an error because the object cannot be found. It seems that the second websocket commandmessage is executed before ExtJS has loaded the file via HTTP-GET.
In my world JavaScript is executed sequentially (I don't use webworkers). I think the call of Ext.create(..) should be executed synchronously with the HTTP-GET in background, shouldn't it?
Here is a "pseudo" trace output of my client application:
ExecuteCommand (Enter): CREATE ("MyClass", "1")
HTTP-GET "MyClass.js"
ExecuteCommand (Enter): DOSTUFF ("1")
ExecuteCommand (Error): DOSTUFF ("1"): Object not found
ExecuteCommand (Enter): CREATE ("MyClass", "1"): OK! Object created!
It makes sense. The GET request is async by default see Firefox doc: https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Synchronous_and_Asynchronous_Requests
To avoid this issue check for object existence and if need be let the server know to resend in a few seconds, and repeat.
Ok, as mentioned by DmitryB the HTTP-GET works asynchronous. After the XMLHttpRequest is sent, the control is passed to the next websocket.onmessage.
I solved this problem by using Ext.require with a callback function before I call Ext.create.
It looks something like that:
ExecuteCommand: CREATE ("MyClass", "1")
Ext.require("MyClass", function(c) {
ExecuteCommand: DOSTUFF (c);
}

Resources