0

I am running 2 java (7) servlets on a tomcat 7 VPS server. One servlet returns a json response and the other servlet returns 4 lines of pure html code.

If I only run the json response servlet, I have no problems handling 12+ million request per day (~140 request per second).

Currently I run only half the traffic on the json servlet (so ~70 request per second).

if I add the servlet which returns html the server response time explodes and returns errors when the number of requests not even reaches 20 per second for this servlet. (So the total amount of requests is ~90 per second).

However if the requests for the html servlet drop to ~2 per minute everything is fine. So it looks like the servlet itself doesn't have any errors.

the two servlets which I am running are:

// Loading required libraries
import java.lang.*;
import java.io.*;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import javax.sql.DataSource;
import javax.naming.*;
import java.util.Random;
import java.math.*;
import java.security.*;
import java.sql.*;

/**
 * Servlet implementation class test
 */
@WebServlet("/html")
public class html extends HttpServlet{
    private static final long serialVersionUID = 12L;

    public html() {
        super();
        // TODO Auto-generated constructor stub
    }
    private DataSource dataSource;

    public void init() throws ServletException {
        try {
            // Get DataSource
            Context initContext  = new InitialContext();
            Context envContext  = (Context)initContext.lookup("java:/comp/env");
            dataSource = (DataSource)envContext.lookup("jdbc/testdb");
            System.out.println("Obtained Cached Data Source ");

        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
            throws ServletException, IOException
  {

      // Get all parameters from request.

      String source = null;
      String UA = null;
      String id = null;
      String ip = null;

      source = request.getParameter("source");
      UA = request.getHeader("User-Agent");
      //is client behind something?
      ip = request.getHeader("X-FORWARDED-FOR");  
      if (ip == null) {  
           ip = request.getRemoteAddr();  
       }


      Connection conn = null;
      Statement stmt = null;
      int exit = 0;

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      try{
         // Open a connection
         conn = dataSource.getConnection();
         stmt = conn.createStatement();
         // Execute SQL query
         stmt = conn.createStatement();
         String sql;
         sql =  "SELECT data from db";
         ResultSet rs = stmt.executeQuery(sql);

         rs.next();
         String url = rs.getString("url");
         String url_src = rs.getString("url_src");



    out.println("<html>");
    out.println("<body>");
    out.println("<a href=\"url\">");
    out.println("<img src=\"url_src\"/></a>");
    out.println("</body></html>");


     long time = System.currentTimeMillis()/1000;

     Random randomGenerator = new Random();
     int randomInt = randomGenerator.nextInt(250);
     String toEnc =  ip + time + UA + randomInt; // Value to hash
     MessageDigest mdEnc = MessageDigest.getInstance("MD5"); 
     mdEnc.update(toEnc.getBytes(), 0, toEnc.length());
     id = new BigInteger(1, mdEnc.digest()).toString(16);// Hashed

     sql = "insert request";
     stmt.execute(sql);




         // Clean-up environment
         rs.close();
         stmt.close();
         conn.close();
      }catch(SQLException se){
         //Handle errors for JDBC
         se.printStackTrace();
      }catch(Exception e){
         //Handle errors for Class.forName
         e.printStackTrace();
      }finally{
         //finally block used to close resources
         try{
            if(stmt!=null)
               stmt.close();
         }catch(SQLException se2){
         }// nothing we can do
         try{
            if(conn!=null)
            conn.close();
         }catch(SQLException se){
            se.printStackTrace();
         }//end finally try
      } //end try*/

   }
}

And the second servlet is:

// Loading required libraries
import java.lang.*;
import java.io.*;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import javax.sql.DataSource;
import javax.naming.*;
import java.util.Random;
import java.math.*;
import java.security.*;
import java.sql.*;

/**
 * Servlet implementation class test
 */
@WebServlet("/json")
public class json extends HttpServlet{
    private static final long serialVersionUID = 11L;

    public json() {
        super();
        // TODO Auto-generated constructor stub
    }
    private DataSource dataSource;

    public void init() throws ServletException {
        try {
            // Get DataSource
            Context initContext  = new InitialContext();
            Context envContext  = (Context)initContext.lookup("java:/comp/env");
            dataSource = (DataSource)envContext.lookup("jdbc/testdb");
            System.out.println("Obtained Cached Data Source ");

        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
            throws ServletException, IOException
  {

      // Get all parameters from request.

      String source = null;
      String UA = null;
      String id = null;
      String ip = null;


      source = request.getParameter("source");
      UA = request.getParameter("ua");
      ip = request.getParameter("clientIP");

      Connection conn = null;
      Statement stmt = null;
      int exit = 0;

      // Set response content type
      response.setContentType("application/json");
      PrintWriter out = response.getWriter();

      try{
         // Open a connection
         conn = dataSource.getConnection();
         stmt = conn.createStatement();
         // Execute SQL query
         stmt = conn.createStatement();
         String sql;
         sql =  "SELECT * from db";
         ResultSet rs = stmt.executeQuery(sql);

         rs.next();
         String url = rs.getString("url");
         String url_src = rs.getString("url_src");



         String jsonResponse = "{\"url\":\"url\","\"media_url\":\"url_src\"}";
         out.println(jsonResponse);

         long time = System.currentTimeMillis()/1000;

         Random randomGenerator = new Random();
         int randomInt = randomGenerator.nextInt(250);
         String toEnc = ip + time + UA + randomInt; // Value to hash
         MessageDigest mdEnc = MessageDigest.getInstance("MD5"); 
         mdEnc.update(toEnc.getBytes(), 0, toEnc.length());
         id = new BigInteger(1, mdEnc.digest()).toString(16);// Hashed

         sql = "insert request";
         stmt.execute(sql);




         // Clean-up environment
         rs.close();
         stmt.close();
         conn.close();
      }catch(SQLException se){
         //Handle errors for JDBC
         se.printStackTrace();
      }catch(Exception e){
         //Handle errors for Class.forName
         e.printStackTrace();
      }finally{
         //finally block used to close resources
         try{
            if(stmt!=null)
               stmt.close();
         }catch(SQLException se2){
         }// nothing we can do
         try{
            if(conn!=null)
            conn.close();
         }catch(SQLException se){
            se.printStackTrace();
         }//end finally try
      } //end try*/

   }
}

I have setup a connection pool with JDBC. And I have the following config.xml file:

<?xml version='1.0' encoding='utf-8'?>
    <!--
  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.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>

    <!-- Default set of monitored resources -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>

    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->

    <!-- Uncomment this to enable Comet connection tacking (provides events
         on session expiration as well as webapp lifecycle) -->
    <!--
    <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
    -->

    <Resource name="jdbc/testdb" 
    auth="Container" 
    type="javax.sql.DataSource" 
    driverClassName="com.mysql.jdbc.Driver" 
    url="jdbc:mysql://localhost:3306/database" 
    username="username" 
    password="password" 
    maxActive="250" 
    maxIdle="60"
    minIdle="30" 
    maxWait="10000"
    poolPreparedStatements="true"
    maxOpenPreparedStatements="100"
    />


</Context>

I have set the envirenment variables or JAVA_OPTS in the file /tomcat/bin/setenv.sh:

export CATALINA_OPTS="-Djava.library.path=/usr/local/apr/lib"

NORMAL="-d64 -Xmx1536m -Xms512m -server"
MAX_PERM_GEN="-XX:MaxPermSize=512m"
HEADLESS="-Djava.awt.headless=true"

JAVA_OPTS="$NORMAL $MAX_PERM_GEN $HEADLESS"
export JAVA_OPTS

My vps has 2gb of ram, with 4 cpu cores of 2ghz. I have tried changing the heap size or the MaxPermSize but it does not seem to matter.

Can someone explain to me what I am doing wrong with the configuration? I don't understand why the server could handle so much load with just one servlet and crashes when the traffic is split over another.

Thanks

user3605780
  • 6,542
  • 13
  • 42
  • 67
  • Hi, the server.xml file is standard. the connector settings are: the default value for the number of threats is 200, this should be more then enough. The average response time in the us is 90ms, so on average only 10-15 threats are needed. Do you have any idea what could cause the high server load when using the servlet which returns the http response? thanks – user3605780 Nov 10 '14 at 18:38
  • `@user3605780` Did you check the argument for maximum connection or maximum accepted connection (or similar properties)? what is your value for `acceptCount`? The default is always 100 which you probably need to change. You said your server.xml is standard. You don't want standard, do you? You have to go through the links I have sent you and find out which one you need to change. – ha9u63a7 Nov 10 '14 at 18:42
  • The acceptCount is indeed standard, however the acceptcount is for when all threats are used. With 200 threats available and an average usage of 15 threats this setting will not matter right? Also when only using the json response servlet the server doesn't give any problems with even double the requests. In numbers: 1) Json servlet only 12+ million requests per day, no problem. ; 2) json servlet 6+ million requests + http servlet 20000 requests per day, no problem; 3) json servlet 6+ million requests + http servlet 1+ million requests, server breaks immidiatly. – user3605780 Nov 10 '14 at 19:03
  • I checked the catalina.out file and found 2 errors which are probably the reason: org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object at org.apache.tomcat.dbcp.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:114) at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044) and: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections" – user3605780 Nov 10 '14 at 20:02
  • However the traffic wasn't that large so is it possible that I have connections leaking somewhere? – user3605780 Nov 10 '14 at 20:04

1 Answers1

0

Hagubear thanks for the info, however the problem was in the couple of out.println() statements. In this thread [1]: Do not use System.out.println in server side code

I found that using this is bad practice. Now I placed it in only 1 out.println() and the server works much better.

thanks

Community
  • 1
  • 1
user3605780
  • 6,542
  • 13
  • 42
  • 67