Author: fhanik Date: Mon Dec 20 16:42:13 2010 New Revision: 1051202 URL: http://svn.apache.org/viewvc?rev=1051202&view=rev Log: Implement a maxConnection threshold for the JIoEndpoint, to be able to constraint how many connections the connector will accept
Added: tomcat/trunk/test/org/apache/catalina/connector/TestMaxConnections.java (with props) Modified: tomcat/trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java tomcat/trunk/webapps/docs/config/http.xml Modified: tomcat/trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java?rev=1051202&r1=1051201&r2=1051202&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java Mon Dec 20 16:42:13 2010 @@ -32,6 +32,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; +import org.apache.tomcat.util.threads.CounterLatch; /** @@ -107,6 +108,8 @@ public class JIoEndpoint extends Abstrac // Not supported return false; } + + protected CounterLatch connectionCounterLatch = null; // ------------------------------------------------ Handler Inner Interface @@ -197,20 +200,24 @@ public class JIoEndpoint extends Abstrac break; } try { + //if we have reached max connections, wait + connectionCounterLatch.await(); // Accept the next incoming connection from the server socket Socket socket = serverSocketFactory.acceptSocket(serverSocket); // Configure the socket if (setSocketOptions(socket)) { - // Hand this socket off to an appropriate processor - if (!processSocket(socket)) { - // Close socket right away - try { - socket.close(); - } catch (IOException e) { - // Ignore + // Hand this socket off to an appropriate processor + if (!processSocket(socket)) { + // Close socket right away + try { + socket.close(); + } catch (IOException e) { + // Ignore + } + } else { + connectionCounterLatch.countUp(); } - } } else { // Close socket right away try { @@ -286,6 +293,7 @@ public class JIoEndpoint extends Abstrac if (log.isTraceEnabled()) { log.trace("Closing socket:"+socket); } + connectionCounterLatch.countDown(); try { socket.getSocket().close(); } catch (IOException e) { @@ -373,6 +381,8 @@ public class JIoEndpoint extends Abstrac if (getExecutor() == null) { createExecutor(); } + + connectionCounterLatch = new CounterLatch(0,getMaxConnections()); // Start acceptor threads for (int i = 0; i < acceptorThreadCount; i++) { @@ -394,6 +404,8 @@ public class JIoEndpoint extends Abstrac @Override public void stopInternal() { + connectionCounterLatch.releaseAll(); + connectionCounterLatch = null; if (!paused) { pause(); } Added: tomcat/trunk/test/org/apache/catalina/connector/TestMaxConnections.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/connector/TestMaxConnections.java?rev=1051202&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/connector/TestMaxConnections.java (added) +++ tomcat/trunk/test/org/apache/catalina/connector/TestMaxConnections.java Mon Dec 20 16:42:13 2010 @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.connector; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.SimpleHttpClient; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; + +public class TestMaxConnections extends TomcatBaseTest{ + + Tomcat tomcat = null; + static int soTimeout = 3000; + static int connectTimeout = 1000; + + public void test1() throws Exception { + init(); + start(); + ConnectThread[] t = new ConnectThread[10]; + int passcount = 0; + int connectfail = 0; + for (int i=0; i<t.length; i++) { + t[i] = new ConnectThread(); + t[i].setName("ConnectThread["+i+"+]"); + t[i].start(); + } + for (int i=0; i<t.length; i++) { + t[i].join(); + if (t[i].passed) passcount++; + if (t[i].connectfailed) connectfail++; + } + assertEquals("The number of successful requests should have been 5.",5, passcount); + assertEquals("The number of failed connects should have been 5.",5, connectfail); + stop(); + } + + + private class ConnectThread extends Thread { + public boolean passed = true; + public boolean connectfailed = false; + public void run() { + try { + TestKeepAliveClient client = new TestKeepAliveClient(); + client.doHttp10Request(); + }catch (Exception x) { + passed = false; + System.err.println(Thread.currentThread().getName()+" Error:"+x.getMessage()); + connectfailed = "connect timed out".equals(x.getMessage()); + } + } + } + + private boolean init; + + private synchronized void init() { + if (init) return; + + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", SimpleHttpClient.TEMP_DIR); + Tomcat.addServlet(root, "Simple", new SimpleServlet()); + root.addServletMapping("/test", "Simple"); + tomcat.getConnector().setProperty("maxKeepAliveRequests", "5"); + tomcat.getConnector().setProperty("soTimeout", "20000"); + tomcat.getConnector().setProperty("keepAliveTimeout", "50000"); + tomcat.getConnector().setProperty("port", "8080"); + tomcat.getConnector().setProperty("maxConnections", "4"); + tomcat.getConnector().setProperty("acceptCount", "1"); + init = true; + } + + private synchronized void start() throws Exception { + tomcat = getTomcatInstance(); + init(); + tomcat.start(); + } + + private synchronized void stop() throws Exception { + tomcat.stop(); + } + + private class TestKeepAliveClient extends SimpleHttpClient { + + private void doHttp10Request() throws Exception { + + long start = System.currentTimeMillis(); + // Open connection + connect(connectTimeout,soTimeout); + + // Send request in two parts + String[] request = new String[1]; + request[0] = + "GET /test HTTP/1.0" + CRLF + CRLF; + setRequest(request); + boolean passed = false; + processRequest(false); // blocks until response has been read + long stop = System.currentTimeMillis(); + System.out.println(Thread.currentThread().getName()+" Request complete:"+(stop-start)+" ms."); + passed = (this.readLine()==null); + // Close the connection + disconnect(); + reset(); + assertTrue(passed); + } + + private void doHttp11Request() throws Exception { + Tomcat tomcat = getTomcatInstance(); + init(); + tomcat.start(); + // Open connection + connect(); + + // Send request in two parts + String[] request = new String[1]; + request[0] = + "GET /test HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Connection: Keep-Alive" + CRLF+ + "Keep-Alive: 300"+ CRLF+ CRLF; + + setRequest(request); + + for (int i=0; i<5; i++) { + processRequest(false); // blocks until response has been read + assertTrue(getResponseLine()!=null && getResponseLine().trim().startsWith("HTTP/1.1 200")); + } + boolean passed = (this.readLine()==null); + // Close the connection + disconnect(); + reset(); + tomcat.stop(); + assertTrue(passed); + } + + @Override + public boolean isResponseBodyOK() { + return true; + } + + } + + + private static class SimpleServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + Thread.sleep(TestMaxConnections.soTimeout/2); + }catch (InterruptedException x) { + + } + resp.setContentLength(0); + resp.flushBuffer(); + } + + } + +} Propchange: tomcat/trunk/test/org/apache/catalina/connector/TestMaxConnections.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java?rev=1051202&r1=1051201&r2=1051202&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java (original) +++ tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java Mon Dec 20 16:42:13 2010 @@ -25,7 +25,9 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; +import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -109,14 +111,20 @@ public abstract class SimpleHttpClient { return null; } - public void connect() throws UnknownHostException, IOException { - socket = new Socket("localhost", port); + public void connect(int connectTimeout, int soTimeout) throws UnknownHostException, IOException { + SocketAddress addr = new InetSocketAddress("localhost", port); + socket = new Socket(); + socket.setSoTimeout(soTimeout); + socket.connect(addr,connectTimeout); OutputStream os = socket.getOutputStream(); writer = new OutputStreamWriter(os); InputStream is = socket.getInputStream(); Reader r = new InputStreamReader(is); reader = new BufferedReader(r); } + public void connect() throws UnknownHostException, IOException { + connect(0,0); + } public void processRequest() throws IOException, InterruptedException { processRequest(true); Modified: tomcat/trunk/webapps/docs/config/http.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/http.xml?rev=1051202&r1=1051201&r2=1051202&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/http.xml (original) +++ tomcat/trunk/webapps/docs/config/http.xml Mon Dec 20 16:42:13 2010 @@ -361,6 +361,15 @@ execute tasks using the executor rather than an internal thread pool.</p> </attribute> + <attribute name="maxConnections" required="false"> + <p>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 reach below this value. The operating system may still accept connections based + on the <code>acceptCount</code> setting. + This setting is currently only applicable to the blocking Java connectors (AJP/HTTP). + Default value is <code>10000</code>.</p> + </attribute> + <attribute name="maxTrailerSize" required="false"> <p>Limits the total length of trailing headers in the last chunk of a chunked HTTP request. If the value is <code>-1</code>, no limit will be --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org