7

I wrote very simple controller which test Servlet 3 features:

@Autowired
    ThreadPoolTaskExecutor taskExecutor;

    @RequestMapping(value="{name}", method = RequestMethod.GET)
    public @ResponseBody DeferredResult<MyResponse> getShopInJSON(@PathVariable String name) {

        DeferredResult<MyResponse> df = new DeferredResult<MyResponse>();
        taskExecutor.submit(new MyRunnable(df));    

        return df; 
    }

In separate Thread I'm doing nothing but 5 second sleep command and after it I return MyResult POJO to DeferredResult.

My web.xml file is according to Servlet 3 specifications:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0"
         metadata-complete="true">
  <display-name>Archetype Created Web Application</display-name>
    <servlet>
    <async-supported>true</async-supported>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

My Connector tomcat is the following:

 <Connector port="8080" protocol="HTTP/1.1"
                maxThreads="5"
                acceptCount="5"
                connectionTimeout="20000"
                redirectPort="8443" />

Now this is the interesting part. When running simple program which opens 10 concurrent connection I see that only 5 connections are served first and second 5 connections are served after first set is released (You can see it from time stemps). This is not how Servlet 3.0 should behave

Fri May 31 01:17:57 IDT 2013: Preparing 10 concurrent connections
Fri May 31 01:18:02 IDT 2013: Output from Server int thread 9 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:02 IDT 2013: Output from Server int thread 8 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:02 IDT 2013: Output from Server int thread 4 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:02 IDT 2013: Output from Server int thread 7 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:02 IDT 2013: Output from Server int thread 2 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:07 IDT 2013: Output from Server int thread 1 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:07 IDT 2013: Output from Server int thread 0 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:07 IDT 2013: Output from Server int thread 5 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:07 IDT 2013: Output from Server int thread 6 :{"props1":"param1","props2":"param1"}
Fri May 31 01:18:07 IDT 2013: Output from Server int thread 3 :{"props1":"param1","props2":"param1"}

If change Tomcat Connector to

   <Connector connectionTimeout="200000" maxThreads="5" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>

it works like charm. I don't want to do it. According to Tomcat docs I should receive Servlet 3.0 functionality without Http11NioProtocol connector.

What is wrong?

danny.lesnik
  • 18,479
  • 29
  • 135
  • 200
  • http://tikalk.com/java/servlet-3-support-spring-mvc-32 talks about the same issue you mentioned. – 2020 May 31 '13 at 18:26
  • 1
    @brainOverflow, thank you, but I'm the author of this post :) I just think that this behavior is abnormal, according to Tomcat docs Servlet 3 supposed to work on BIO as well. – danny.lesnik May 31 '13 at 18:48
  • oh ok...i just thought of showing some support to your claim by citing that post... :-) by the way, I came across this other [post](http://tomcat.10.x6.nabble.com/Tomcat-7-Servlet-3-0-and-Non-Blocking-td2120806.html) where they say: `All connectors release the thread to handle other requests once startAsync has been called.` Have you tried this with AsyncContext instead of DeferredResult ? May be spring is introducing some behavior here ! – 2020 May 31 '13 at 20:15
  • @brainOverflow, DeferredResult is just wrapper. It has AsyncContext and is calling startAsync() method on the request. The fact that it behaves correct using NIO connector tells us that it is more likely the Tomcat behavior. – danny.lesnik May 31 '13 at 20:41

2 Answers2

7

The problem is due to the maxThreads=5 setting in your Tomcat config.

For the non-NIO case, this setting does not only restrict the maximum number of request processing threads but also restricts the maximum number of connections !

Since you have not specified maxConnections, it is choosing a default value for maxConnections. Here is the excerpt from Tomcat doc on how it chooses the default value for maxConnections:

maxConnections : The maximum number of connections that the server will accept and process at any given time. When this number has been reached, the server will not accept any more connections until the number of connections falls below this value. The operating system may still accept connections based on the acceptCount setting. Default value varies by connector type. For BIO the default is the value of maxThreads unless an Executor is used in which case the default will be the value of maxThreads from the executor. For NIO the default is 10000. For APR/native, the default is 8192.

You can explicitly specify a maxConnections="10" (for example) setting to override this default behavior. You should then see that you can get 10 parallel requests getting processed regardless of the Connector used. I tried this and it works.

2020
  • 2,821
  • 2
  • 23
  • 40
0

Have you tried increasing maxThreads to 10? It looks like your Tomcat connector is capped at 5 threads

gerrytan
  • 40,313
  • 9
  • 84
  • 99
  • that's true and according to Servlet 3.0 specification those 5 threads should be released immediately. That's the way to test asynchronous. – danny.lesnik May 31 '13 at 03:21
  • So what are you expecting, those tomcat worker thread to be released before your JSON response is returned? Then how would it return it in the future if the thread is already gone? Maybe it's asynchronous in the sense that your servlet method can return a "promise" it will write the response in the future and then exits. However the tomcat worker thread has to be alive until this promise is fulfilled – gerrytan May 31 '13 at 06:19
  • Maybe at least that's how `protocol="HTTP/1.1"` tomcat thread works – gerrytan May 31 '13 at 06:28
  • 1
    I expect that all 10 results will return in the same time (5 sec) after it was submitted and this is due to the fact, that Tomcat will proceed 5 request pass it to another worker and immediately proceed to another 5 request and I'm getting this behavior while using nio connector. – danny.lesnik May 31 '13 at 07:03