【问题标题】:Why does my server crash when I run more than 1 almost identicall java servlets?当我运行超过 1 个几乎相同的 java servlet 时,为什么我的服务器会崩溃?
【发布时间】:2014-11-10 15:23:15
【问题描述】:

我在 tomcat 7 VPS 服务器上运行 2 个 java (7) servlet。一个 servlet 返回一个 json 响应,另一个 servlet 返回 4 行纯 html 代码。

如果我只运行 json 响应 servlet,我每天处理 12+ 百万个请求(每秒约 140 个请求)没有问题。

目前我只在 json servlet 上运行一半的流量(所以每秒约 70 个请求)。

如果我添加返回 html 的 servlet,当该 servlet 的请求数甚至没有达到每秒 20 个时,服务器响应时间会爆炸并返回错误。 (所以请求总数约为每秒 90 个)。

但是,如果对 html servlet 的请求下降到每分钟约 2 个,一切都很好。所以看起来servlet本身没有任何错误。

我正在运行的两个 servlet 是:

// 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*/

   }
}

第二个servlet是:

// 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*/

   }
}

我已经用 JDBC 建立了一个连接池。我有以下 config.xml 文件:

<?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>

我已经在文件/tomcat/bin/setenv.sh中设置了环境变量或者JAVA_OPTS:

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

我的 vps 有 2gb 的内存,有 4 个 2ghz 的 cpu 核心。我尝试过更改堆大小或 MaxPermSize,但这似乎并不重要。

有人可以向我解释我在配置方面做错了什么吗?我不明白为什么服务器可以只用一个 servlet 处理如此多的负载,而当流量分散到另一个时会崩溃。

谢谢

【问题讨论】:

  • 您好,server.xml 文件是标准的。连接器设置为: 威胁数量的默认值为 200,这应该足够了。美国的平均响应时间为 90 毫秒,因此平均只需要 10-15 个威胁。您知道在使用返回 http 响应的 servlet 时会导致服务器负载高的原因吗?谢谢
  • @user3605780 您是否检查了最大连接或最大接受连接(或类似属性)的参数?你对acceptCount 的价值是多少?默认值始终为 100,您可能需要更改它。你说你的 server.xml 是标准的。你不想要标准,是吗?您必须浏览我发送给您的链接并找出您需要更改的链接。
  • acceptCount 确实是标准的,但是acceptcount 是针对使用所有威胁时的。有 200 种威胁可用,平均使用 15 种威胁,此设置无关紧要,对吧?此外,当仅使用 json 响应 servlet 时,即使请求加倍,服务器也不会出现任何问题。在数字上:1)Json servlet 每天只有 12+ 百万个请求,没问题。 ; 2)json servlet 6+ 百万请求 + http servlet 每天20000请求,没问题; 3) json servlet 6+ 百万请求 + http servlet 1+ 百万请求,服务器立即中断。
  • 我检查了 catalina.out 文件,发现 2 个错误可能是原因: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) 在 org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044) 和:com.mysql.jdbc.exceptions .jdbc4.MySQLNonTransientConnectionException:数据源拒绝建立连接,来自服务器的消息:“连接太多”
  • 但是流量不是很大,所以我的连接有可能在某处泄漏?

标签: java tomcat servlets config


【解决方案1】:

Hagubear 感谢您提供信息,但问题出在几个 out.println() 语句中。在这个线程[1]:Do not use System.out.println in server side code

我发现使用它是不好的做法。现在我只将它放在 1 out.println() 中,服务器工作得更好。

谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多