2011-04-27 3 views
18

Tomcat 6.0.29을 Tomcat 7의 연결 풀 및 MySQL과 함께 사용하고 있습니다. 응용 프로그램을 테스트하면 풀에서 아무 것도 재사용하지 않지만 풀의 최대 활성 크기가 설정되면 풀에 수면 연결이 수백 개 있기 때문에 최종적으로 데이터베이스를 사용할 수없는 곳으로 새로운 풀을 만듭니다. 20Tomcat 연결 풀이 너무 많아서 잠자기 모드로 고정되었습니다.

참조 여기 참조 : 이것은 아래와 같이 minIdle & INITIALSIZE 속성 각 요청에 대해 정확히 10 작성

+----+------+-----------------+--------+---------+------+-------+------------------+ 
| Id | User | Host   | db  | Command | Time | State | Info    | 
+----+------+-----------------+--------+---------+------+-------+------------------+ 
| 2 | root | localhost:51877 | dbname | Sleep | 9 |  | NULL    | 
| 4 | root | localhost  | NULL | Query | 0 | NULL | show processlist | 
| 5 | root | localhost:49213 | dbname | Sleep | 21 |  | NULL    | 
| 6 | root | localhost:53492 | dbname | Sleep | 21 |  | NULL    | 
| 7 | root | localhost:46012 | dbname | Sleep | 21 |  | NULL    | 
| 8 | root | localhost:34964 | dbname | Sleep | 21 |  | NULL    | 
| 9 | root | localhost:52728 | dbname | Sleep | 21 |  | NULL    | 
| 10 | root | localhost:43782 | dbname | Sleep | 21 |  | NULL    | 
| 11 | root | localhost:38468 | dbname | Sleep | 21 |  | NULL    | 
| 12 | root | localhost:48021 | dbname | Sleep | 21 |  | NULL    | 
| 13 | root | localhost:54854 | dbname | Sleep | 21 |  | NULL    | 
| 14 | root | localhost:41520 | dbname | Sleep | 21 |  | NULL    | 
| 15 | root | localhost:38112 | dbname | Sleep | 13 |  | NULL    | 
| 16 | root | localhost:39168 | dbname | Sleep | 13 |  | NULL    | 
| 17 | root | localhost:40427 | dbname | Sleep | 13 |  | NULL    | 
| 18 | root | localhost:58179 | dbname | Sleep | 13 |  | NULL    | 
| 19 | root | localhost:40957 | dbname | Sleep | 13 |  | NULL    | 
| 20 | root | localhost:45567 | dbname | Sleep | 13 |  | NULL    | 
| 21 | root | localhost:48314 | dbname | Sleep | 13 |  | NULL    | 
| 22 | root | localhost:34546 | dbname | Sleep | 13 |  | NULL    | 
| 23 | root | localhost:44928 | dbname | Sleep | 13 |  | NULL    | 
| 24 | root | localhost:57320 | dbname | Sleep | 13 |  | NULL    | 
| 25 | root | localhost:54643 | dbname | Sleep | 29 |  | NULL    | 
| 26 | root | localhost:49809 | dbname | Sleep | 29 |  | NULL    | 
| 27 | root | localhost:60993 | dbname | Sleep | 29 |  | NULL    | 
| 28 | root | localhost:36676 | dbname | Sleep | 29 |  | NULL    | 
| 29 | root | localhost:53574 | dbname | Sleep | 29 |  | NULL    | 
| 30 | root | localhost:45402 | dbname | Sleep | 29 |  | NULL    | 
| 31 | root | localhost:37632 | dbname | Sleep | 29 |  | NULL    | 
| 32 | root | localhost:56561 | dbname | Sleep | 29 |  | NULL    | 
| 33 | root | localhost:34261 | dbname | Sleep | 29 |  | NULL    | 
| 34 | root | localhost:55221 | dbname | Sleep | 29 |  | NULL    | 
| 35 | root | localhost:39613 | dbname | Sleep | 15 |  | NULL    | 
| 36 | root | localhost:52908 | dbname | Sleep | 15 |  | NULL    | 
| 37 | root | localhost:56401 | dbname | Sleep | 15 |  | NULL    | 
| 38 | root | localhost:44446 | dbname | Sleep | 15 |  | NULL    | 
| 39 | root | localhost:57567 | dbname | Sleep | 15 |  | NULL    | 
| 40 | root | localhost:56445 | dbname | Sleep | 15 |  | NULL    | 
| 41 | root | localhost:39616 | dbname | Sleep | 15 |  | NULL    | 
| 42 | root | localhost:49197 | dbname | Sleep | 15 |  | NULL    | 
| 43 | root | localhost:59916 | dbname | Sleep | 15 |  | NULL    | 
| 44 | root | localhost:37165 | dbname | Sleep | 15 |  | NULL    | 
| 45 | root | localhost:45649 | dbname | Sleep | 1 |  | NULL    | 
| 46 | root | localhost:55397 | dbname | Sleep | 1 |  | NULL    | 
| 47 | root | localhost:34322 | dbname | Sleep | 1 |  | NULL    | 
| 48 | root | localhost:54387 | dbname | Sleep | 1 |  | NULL    | 
| 49 | root | localhost:55147 | dbname | Sleep | 1 |  | NULL    | 
| 50 | root | localhost:47280 | dbname | Sleep | 1 |  | NULL    | 
| 51 | root | localhost:56856 | dbname | Sleep | 1 |  | NULL    | 
| 52 | root | localhost:58369 | dbname | Sleep | 1 |  | NULL    | 
| 53 | root | localhost:33712 | dbname | Sleep | 1 |  | NULL    | 
| 54 | root | localhost:44315 | dbname | Sleep | 1 |  | NULL    | 
| 55 | root | localhost:54649 | dbname | Sleep | 14 |  | NULL    | 
| 56 | root | localhost:41202 | dbname | Sleep | 14 |  | NULL    | 
| 57 | root | localhost:59393 | dbname | Sleep | 14 |  | NULL    | 
| 58 | root | localhost:38304 | dbname | Sleep | 14 |  | NULL    | 
| 59 | root | localhost:34548 | dbname | Sleep | 14 |  | NULL    | 
| 60 | root | localhost:49567 | dbname | Sleep | 14 |  | NULL    | 
| 61 | root | localhost:48077 | dbname | Sleep | 14 |  | NULL    | 
| 62 | root | localhost:48586 | dbname | Sleep | 14 |  | NULL    | 
| 63 | root | localhost:45308 | dbname | Sleep | 14 |  | NULL    | 
| 64 | root | localhost:43169 | dbname | Sleep | 14 |  | NULL    | 

.

다음은 JSP 페이지에 포함 된 샘플 테스트 코드입니다. 코드는 응용 프로그램의 코드가 아니며 문제가 내 코드와 함께 있었는지 확인하는 데 사용되지만 문제는 여전히 지속됩니다. 여기

Context envCtx; 
envCtx = (Context) new InitialContext().lookup("java:comp/env"); 
DataSource datasource = (DataSource) envCtx.lookup("jdbc/dbname"); 
Connection con = null; 

try { 
    con = datasource.getConnection(); 
    Statement st = con.createStatement(); 
    ResultSet rs = st.executeQuery("select * from UserAccount"); 
    int cnt = 1; 
    while (rs.next()) { 
     out.println((cnt++)+". Token:" +rs.getString("UserToken")+ 
     " FirstName:"+rs.getString("FirstName")+" LastName:"+rs.getString("LastName")); 
    } 
    rs.close(); 
    st.close(); 
} finally { 
    if (con!=null) try {con.close();}catch (Exception ignore) {} 
} 

내의 context.xml 파일입니다

<?xml version="1.0" encoding="UTF-8"?> 
<Context> 
    <Resource name="jdbc/dbname" 
       auth="Container" 
       type="javax.sql.DataSource" 
       factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" 
       testWhileIdle="true" 
       testOnBorrow="true" 
       testOnReturn="false" 
       validationQuery="SELECT 1" 
       validationInterval="30000" 
       timeBetweenEvictionRunsMillis="30000" 
       maxActive="20" 
       minIdle="10" 
       maxWait="10000" 
       initialSize="10" 
       removeAbandonedTimeout="60" 
       removeAbandoned="true" 
       logAbandoned="true" 
       minEvictableIdleTimeMillis="30000" 
       jmxEnabled="true" 
       jdbcInterceptors= 
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" 
       username="" 
       password="" 
       driverClassName="com.mysql.jdbc.Driver" 
       url="jdbc:mysql://localhost:3306/dbname?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf8"/> 

<WatchedResource>WEB-INF/web.xml</WatchedResource> 
<WatchedResource>META-INF/context.xml</WatchedResource> 
</Context> 

내가 낮은 숫자로 removeAbandonedTimeout을 사용할 수 있습니다 확신하고 모든 자고 연결을 제거 것입니다,하지만이 해결되지 것입니다 진짜 문제일까요? 아무도 내가 뭘 잘못하고 있는지 알아? 고맙습니다.

+0

try/catch에서 코드가 예외를 던지고 있습니까? 현재로서는 무시하고 가능한 출력 만 있습니다. – Mikaveli

+0

그건 단지 아파치의 Tomcat 연결 풀 문서에서 복사 한 것입니다. 정상적인 try catch 문을 가진 내 응용 프로그램은이 문제가 발생 함에도 불구하고 예외를 throw하지 않습니다. – meatyowllegs

+0

은 dev 환경이나 어디에서 뜨거운 배포 및/또는 재배포가 이루어지는 곳에서 일어나는 일입니까? – Augusto

답변

11

이 환경을 테스트 할 환경이 없는데, 현재 은 각 쿼리 후에 Connection, Statement 및 ResultSet을 닫아야한다고 믿습니다. 이러한 누출 중 하나가 있으면 Connection이 유휴 상태 (반드시 풀로 반환되지는 않음) 상태로 매달릴 수 있습니다.

받는 Connection 개체는 실제로 풀링 계층의 프록시 일종이어야합니다. close을 호출하면 해당 연결에서 "예약"이 해제되고 풀에 반환됩니다.

열려있을 수 있으므로 (일반적으로) 닫히지 않은 Statement 또는 ResultSets는 풀 계층에 의해 여전히 "사용 중"이라는 표시로 해석 될 수 있습니다. "

런타임시 상태를 확인하기 위해 Connection 객체를 검사하여 (예 : 디버거가 쉽게 만들 수 있음)이를 확인할 수 있습니다.

단순화 (...)하기 위해 각 데이터베이스 연결 호출 후 finally 블록에서 다음과 같이 불쾌한 작은 루틴을 사용했습니다 : … finally { closeAll (rs, st, con); }, 즉시 컨텍스트에서 벗어날 수 있도록합니다.

/** 
* Close a bunch of things carefully, ignoring exceptions. The 
* “things” supported, thus far, are: 
* <ul> 
* <li>JDBC ResultSet</li> 
* <li>JDBC Statement</li> 
* <li>JDBC Connection</li> 
* <li>Lock:s</li> 
* </ul> 
* <p> 
* This is mostly meant for “finally” clauses. 
* 
* @param things A set of SQL statements, result sets, and database 
*   connections 
*/ 
public static void closeAll (final Object... things) { 
    for (final Object thing : things) { 
     if (null != thing) { 
      try { 
       if (thing instanceof ResultSet) { 
        try { 
         ((ResultSet) thing).close(); 
        } catch (final SQLException e) { 
         /* No Op */ 
        } 
       } 
       if (thing instanceof Statement) { 
        try { 
         ((Statement) thing).close(); 
        } catch (final SQLException e) { 
         /* No Op */ 
        } 
       } 
       if (thing instanceof Connection) { 
        try { 
         ((Connection) thing).close(); 
        } catch (final SQLException e) { 
         /* No Op */ 
        } 
       } 
       if (thing instanceof Lock) { 
        try { 
         ((Lock) thing).unlock(); 
        } catch (final IllegalMonitorStateException e) { 
         /* No Op */ 
        } 
       } 
      } catch (final RuntimeException e) { 
       /* No Op */ 
      } 
     } 
    } 
} 

이 아무도 if (null != con) { try { con.close() } catch (SQLException e) {} }의 이상 추악한 스 D 자에 넣어 깜빡하지 않도록 단지 문법 설탕했다 (보통의 ResultSet, 계산서, 및 연결에 대한 3 회 반복) 우리의 포매터가 데이터베이스를 건드린 코드 블록마다 부수적 인 정리 코드의 전체 화면으로 전환하는 것의 "시각적 잡음"을 제거했습니다.

(거기에는지원이 데이터베이스와 관련성이 거의없는 잠재적 인 예외에 대한 일부 관련되었지만 불쾌한 데드락 상태가 있었지만 유사한 방식으로 회선 잡음을 줄이기 위해 사용되었습니다 어떤 스레드 동기화 코드에서. 이것은 게임 객체와 SQL 테이블을 조작하려고 시도 할 때 4,000 개의 활성 스레드를 가질 수있는 MMO 서버에서 가져온 것입니다.)

+0

+1 연결 풀에 연결을 해제하려면 반드시 연결을 닫아야합니다 –

2

연결 풀의 maxAge 속성을 확인하십시오. (나는 당신이 그것을 설정하지 않았다 나타났습니다.)

MAXAGE이

이 연결을 유지하는 시간 (밀리 초)입니다. 이 풀로 반환되면 풀은 이제 time-when-connected> maxAge에 도달했는지 확인하고 만약 그렇다면 풀에 반환하지 않고 연결을 닫습니다 ( ). 기본값 은 0입니다. 이는 연결이 열린 상태로 유지되며 연결을 풀로 반환 할 때 검사를 수행하지 않을 것임을 의미합니다. [source]

기본적으로 이렇게하면 수면 스레드가 복구되어 문제를 해결할 수 있습니다.

1

아마도 DBCP 연결 풀 문서에서이 노트는 해답이 될 수 있습니다

참고 : maxIdle가 과도하게로드 시스템에서 너무 낮게 설정하면 당신은 연결이 폐쇄되는 것을 볼과 거의 즉시 새로운 연결됩니다 수 있습니다 열리고있다. 이는 활성 스레드가 연결을 열 때보다 빠르게 잠시 닫음으로써 유휴 연결 수를 maxIdle 이상으로 증가시킨 결과입니다. 부하가 심한 시스템에 대한 maxIdle의 최적 값은 다양하지만 기본값은 좋은 출발점입니다.

아마도 maxIdle은 == maxActive + minIdle이어야합니다.

0

연결 제공자와 함께 시도 할 때마다 모든 호출을 찾는 대신 정적으로 선언 된 데이터 소스 제공자가 포함될 클래스를 작성해야합니다. InitialContext와 같습니다. 매번 새로운 인스턴스를 생성하기 때문일 수도 있습니다.

1

코드에 대한 간단한 설명 : Connection뿐만 아니라 ResultSet 및 Statement도 Finally 블록에서 닫아야합니다. BRPocock에 의해 주어진 방법은 잘 작동합니다.

그러나 요청 당 10 개의 연결에 대한 실제 이유가 아닙니다. minIdle을 10으로 설정 했으므로 각 요청마다 10 개의 연결을 얻는 이유는 작성시 각 데이터 소스가 10 개의 연결을 갖도록 강제하기 때문입니다. (5 minIdle을 설정하기 위해 시도하고 당신은 당신이 요청에 따라 5 연결이있을 것이라는 점을 참조하십시오.)

문제를 귀하의 경우, 때마다 요청을 수행하는 것이, 새 데이터 소스 생성된다

DataSource datasource = (DataSource) envCtx.lookup("jdbc/dbname"); 

정확히 조회가 어떻게 작동하는지 모르겠지만 mysql에서 processlist를 받으면 나는 모든 요청에 ​​대해 새로운 데이터 소스를 생성한다는 것을 확신합니다. 자바 서블릿이 있다면 메인 서블릿의 init() 메소드에서 DataSource를 생성해야한다. 거기에서 당신은 그때부터 그것으로부터 연결을 얻을 수 있습니다.

데이터 소스가에 의존
private DataSource getDataSource(String db, String user, String pass) 
{ 
    for(Map.Entry<String, DataSource> entry : datasources.entrySet()) 
    { 
     DataSource ds = entry.getValue(); 
     if(db.equals(ds.getPoolProperties().getUrl())) 
     { 
      return ds; 
     } 
    } 
    System.out.println("NEW DATASOURCE CREATED ON REQUEST: " + db); 

    DataSource ds = new DataSource(initPoolProperties(db, user, pass)); 
    datasources.put(db, ds); 
    return ds; 
} 

정말 빠르지 방법과 동일 : 나는 내 데이터 소스를 얻기 위해 다음 코드를 사용하여 여러 데이터 소스 (여러 데이터베이스)를 가지고 있기 때문에 뭔가 다른 한 내 경우

, ,하지만 그것은 작동합니다. 난 그냥 내 데이터 소스를 포함하는 글로벌 HashMap을 유지하고 아직 존재하지 않는 데이터 소스를 요청하면 새 데이터 소스를 만듭니다.로그에서 단지 NEW DATASOURCE CREATED ON REQUEST: dbname 메시지가 데이터베이스마다 한 번만 표시되기 때문에 심지어 여러 클라이언트가 동일한 데이터 소스를 사용하기 때문에 이것이 잘 작동한다는 것을 알고 있습니다.

0

최대 절전 모드를 사용하고 일부 메서드에 @Transactional으로 주석을 지정하지 못했기 때문에이 문제가 발생했습니다. 연결은 결코 수영장으로 돌아 가지 않았습니다.

0

이 문제는 응용 프로그램을 다시로드 (Resource Killing)하지 않아 발생합니다. 그리고 응용 프로그램 컨텍스트 리소스가 아직 살아 있습니다. 당신이 /Catalina/localhost/.xml을 삭제하지 않는 한이 문제를 해결하고 다시 또는 코드 문제

참고 : 아무것도를 다시 시작하지 tomcat7에 :: 서비스 서비스를 다시 시작하고 더 자주 넣어 결코 ...하지 않다가있다, 구성에 아무런 문제가 없습니다 ..

응원 ~

관련 문제