Since a servlet is blocking, a normal HTTP communication makes the thread wait. So there is a possibility that all the threads in the pool can be used up. By using the asynchronous http client framework Asynchronous HTTP Client framework (AHC javadocs), the thread can be released back into the pool and be made available for other purposes until the answer comes back. When the answer comes back, a callback is fired which then relays the response back to the client.
...
The http-local-app
The AsyncServlet in this app implements app uses the AHC and implements the CometProcessor interface. This will The CometProcessor will make it's event method invoked rather than the usual service method, according to the event which occurred. The event object gives access to the usual request and response objects, which may be used in the usual way. More information about this can be read here.
...
- Stop the server.
- edit geronimo_home/var/config/config.xml
- add
load="false"
to theTomcatWebConnector
gbean. The line would now look like this
<gbean load="false" name="TomcatWebConnector">
- for the module
org.apache.geronimo.configs/tomcat6/<version>/car
, add the following gbeanCode Block XML XML borderStyle solid <gbean gbeanInfo="org.apache.geronimo.tomcat.connector.Http11NIOConnectorGBean" name="org.apache.geronimo.configs/tomcat6/2.0.1/car?ServiceModule=org.apache.geronimo.configs/tomcat6/2.0.1/car,j2eeType=GBean,name=TomcatNIOConnector"> <attribute name="useExecutor">true</attribute> <attribute name="maxThreads">40</attribute> <attribute name="pollerThreadCount">1</attribute> <attribute name="socket_soTimeout">5000</attribute> <attribute name="selectorTimeout">1000</attribute> <attribute name="connectionTimeout">60000</attribute> <attribute name="socket_soLingerOn">true</attribute> <attribute name="acceptorThreadCount">1</attribute> <attribute name="port">8080</attribute> <attribute name="pollerThreadPriority">5</attribute> <attribute name="compressableMimeType">text/html,text/xml,text/plain</attribute> <attribute name="socket_performanceLatency">0</attribute> <attribute name="useComet">true</attribute> <attribute name="useIPVHosts">false</attribute> <attribute name="maxHttpHeaderSize">4096</attribute> <attribute name="restrictedUserAgents" value=""/> <attribute name="acceptorThreadPriority">5</attribute> <attribute name="proxyPort">0</attribute> <attribute name="maxPostSize">2097152</attribute> <attribute name="minSpareThreads">10</attribute> <attribute name="socketBuffer">9000</attribute> <attribute name="redirectPort">8443</attribute> <attribute name="bufferSize">2048</attribute> <attribute name="socket_soReuseAddress">false</attribute> <attribute name="useSendfile">true</attribute> <attribute name="threadPriority">5</attribute> <attribute name="socket_tcpNoDelay">false</attribute> <attribute name="useBodyEncodingForURI">false</attribute> <attribute name="maxSpareThreads">100</attribute> <attribute name="enableLookups">true</attribute> <attribute name="socket_performanceConnectionTime">1</attribute> <attribute name="socket_appWriteBufSize">8192</attribute> <attribute name="maxKeepAliveRequests">100</attribute> <attribute name="socket_ooBInline">true</attribute> <attribute name="socket_keyCache">500</attribute> <attribute name="disableUploadTimeout">true</attribute> <attribute name="socket_bufferPoolSize">104857600</attribute> <attribute name="keepAliveTimeout">60000</attribute> <attribute name="acceptCount">100</attribute> <attribute name="socket_txBufSize">43800</attribute> <attribute name="connectionLinger">-1</attribute> <attribute name="command_line_options">true</attribute> <attribute name="tcpNoDelay">true</attribute> <attribute name="oomParachute">1048576</attribute> <attribute name="socket_appReadBufSize">8192</attribute> <attribute name="selectorPool_maxSpareSelectors">-1</attribute> <attribute name="xpoweredBy">false</attribute> <attribute name="socket_rxBufSize">25188</attribute> <attribute name="maxSavePostSize">4096</attribute> <attribute name="socket_soLingerTime">25</attribute> <attribute name="compression">off</attribute> <attribute name="host">0.0.0.0</attribute> <attribute name="socket_eventCache">500</attribute> <attribute name="socket_performanceBandwidth">1</attribute> <attribute name="noCompressionUserAgents" value=""/> <attribute name="name">TomcatNIOConnector</attribute> <attribute name="socket_soKeepAlive">false</attribute> <attribute name="socket_soTrafficClass">28</attribute> <attribute name="socket_directBuffer">false</attribute> <attribute name="selectorPool_maxSelectors">200</attribute> <attribute name="emptySessionPath">false</attribute> <attribute name="uriEncoding">ISO-8859-1</attribute> <attribute name="allowTrace">false</attribute> <attribute name="processCache">200</attribute> <attribute name="socket_processorCache">500</attribute> <attribute name="server" null="true"/> <attribute name="proxyName" null="true"/> <reference name="TomcatContainer"> <pattern> <groupId>org.apache.geronimo.configs</groupId> <artifactId>tomcat6</artifactId> <version>2.0.1</version> <type>car</type> <name>TomcatWebContainer</name> </pattern> </reference> <reference name="ServerInfo"> <pattern> <groupId>org.apache.geronimo.configs</groupId> <artifactId>j2ee-system</artifactId> <version>2.0.1</version> <type>car</type> <name>ServerInfo</name> </pattern> </reference> </gbean>
- The gbean uses version 2.0.1. Replace that with the appropriate version of your server
- Start the server.
...
- Deploy samples\async-http\http-local-app\target\http-local-app-2.0-SNAPSHOT.war on the Geronimo server we configured earlier using either the consoleor the command line
$geronimo_home/bin>./deploy.sh deploy samples\async-http\http-local-app\target\http-local-app-2.0-SNAPSHOT.war
- Deploy samples\async-http directory\http-remote-app\target\http-remote-app-2.0-SNAPSHOT.war on any server on the remote machine.
- Point your browser to http://localhost:8080/localApp/async. If you wish to override the remoteUrl set in the <init-param> of the web.xml, you may pass it as a request param (http://localhost:8080/localApp/async?remoteUrl=http://foo:8080)
- You should see either a dummy html or one of the popular HTTP errors.
...
- Download the client python script. You will need python installed on your machine to run this script. You can download Python from here.
- The script takes 3 arguments - the name of the machine running http-local-app (AHC), the url of the server on the remote app and the number of threads.
ahcrequest.py local_machine http://remote_machine:8080
200
Test Results
The following test results show a comparison between an AHC enhanced servlet and a normal blocking servlet. Each of these servlets are pounded by 16, 200 and 500 simultaneous requests. The output captured from the python script shows the response time for each request. These tests also demonstrate the significance of tuning the IP stacks. When none of the machines are tuned, the response time starts increasing as the number of threads start increasing. Executing a netstat
command shows that most sockets are either in TIME_WAIT or CLOSED_WAIT state.
Threads | Blocking Servlet | AHC Servlet | HttpComponents | ||
---|---|---|---|---|---|
16 | |||||
100 |
|
| |||
200 | 25.935 200 | ||||
400 |
|
| |||
500 |
Code location and References
Browse the sample code at http://svn.apache.org/viewvc/geronimo/samples/trunk/samples/async-http/
Check out code from svn using
svn co https://svn.apache.org/repos/asf/geronimo/samples/trunk/samples/async-http async-http
Browse the AHC code at http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient
Check out code from svn using
svn co https://svn.apache.org/repos/asf/geronimo/sandbox/AsyncHttpClient ahc
View AHC's javadocs at AHC javadocs