Tomcat 5.5.23 文档阅读Tips 8 JNDI Datasource

本文特别对Datasource类型的JNDI的配置做了解说。 

1. Tomcat对DataSource类型的资源采用的是DBCP。DBCP支持JDBC 2.0, 在安装了JVM 1.4版本或更高的情况下,DBCP支持JDBC 3.0。 

2. DBCP需要以下三个component,他们都来自Apache Jakarta Common Project,他们是: 

Jakarta-Commons DBCP 
Jakarta-Commons Collections 
Jakarta-Commons Pool 

这三个component都被包装在了$CATALINA_HOME/common/lib/naming-factory-dbcp.jar这个jar文件中。 

3. Preventing DB Pool leaks. 这个很不错,DBCP为了防止应用程序忘记关闭ResultSets,Statements, Connections,特意设计了这样一个功能。DBCP能帮我们关闭不活动的connections,而且还能打印出stack trace,报告说是哪个class使用了connection,却没有关闭它。具体配置如下: 

在定义DataSource的Resource定义中,加入attribute定义: 

removeAbandoned="true" 

这个属性默认配置为false。这样DBCP就可以关闭那些可能被应用程序忘记关闭的connection,具体的timeout设定这样配置: 

removeAbandonedTimeout="60" 

也就是说,如果DBCP发现一个connection 60秒内处于不活动状态,DBCP便自动回收。这个配置的默认值是300秒,也就是5分钟。 

此外,还可以加入这个属性定义: 

logAbandoned="true" 

这样DBCP就会打印stack trace,让我们看到是哪个class忘记关闭connection。这个属性默认配置为false。
4. 文档给出了一个MySQL DataSource配置的例子。 

(1) 将mysql jdbc的jar包放到$CATALINA_HOME/common/lib目录下。目前MySQL提供的官方驱动是Connector/J,目前EasyCluster中使用的是Connector/J 3.1.12版本,可以稳定工作。 

(2) 在Tomcat中配置DataSource资源,以下例子给出的是在Context中的配置,也就说,只能给这个web app使用: 

CODE: SELECT ALL
<Context path="/DBTest" docBase="DBTest"
        debug="5" reloadable="true" crossContext="true">

    <!-- maxActive: Maximum number of dB connections in pool. Make sure you
         configure your mysqld max_connections large enough to handle
         all of your db connections. Set to 0 for no limit.
         -->

    <!-- maxIdle: Maximum number of idle dB connections to retain in pool.
         Set to -1 for no limit.  See also the DBCP documentation on this
         and the minEvictableIdleTimeMillis configuration parameter.
         -->

    <!-- maxWait: Maximum time to wait for a dB connection to become available
         in ms, in this example 10 seconds. An Exception is thrown if
         this timeout is exceeded.  Set to -1 to wait indefinitely.
         -->

    <!-- username and password: MySQL dB username and password for dB connections  -->

    <!-- driverClassName: Class name for the old mm.mysql JDBC driver is
         org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
         Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
         -->
    
    <!-- url: The JDBC connection url for connecting to your MySQL dB.
         The autoReconnect=true argument to the url makes sure that the
         mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
         connection.  mysqld by default closes idle connections after 8 hours.
         -->

  <Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
               maxActive="100" maxIdle="30" maxWait="10000"
               username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/javatest?autoReconnect=true"/>

</Context>


(3) 配置web.xml

CODE: SELECT ALL
<resource-ref>
      <description>DB Connection</description>
      <res-ref-name>jdbc/TestDB</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
</resource-ref>


(4) OK, 书写测试代码:

CODE: SELECT ALL
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<sql:query var="rs" dataSource="jdbc/TestDB">
select id, foo, bar from testdata
</sql:query>

<html>
  <head>
    <title>DB Test</title>
  </head>
  <body>

  <h2>Results</h2>
  
<c:forEach var="row" items="${rs.rows}">
    Foo ${row.foo}<br/>
    Bar ${row.bar}<br/>
</c:forEach>

  </body>
</html>
5. Common Problems I. 第一个常见问题就是“间歇性的取connection失败”。Tomcat分析说这是由于GC导致的。也就说,当GC发生的时候,Tomcat处于停止状态,如果GC花费的时间过长,长到超过了DBCP中配置的maxWait的时间,这种问题就会出现。DBCP中的maxWait表示的是当超过这个时间后,DBCP就认为取connection失败,这个值用毫秒表示。加入GC花费了15秒的时间(当然一般GC如果花费了10s就表示已经很恐怖了),而maxWait设定成10s的话,就会出现上面的问题了。解决方案是启动tomcat的时候加上-verbose:gc参数,观察每次gc花费的时间,然后微调maxWait或其他东西。 

6. Common Problems II. Random Connection Close Exception. 这种问题EasyCluster中没有,他是这样产生的: 

(1) 请求1请求了一个连接,用完后调用了close,此时JVM切换线程到请求2 
(2) 请求2请求了一个连接,由于请求1已经用完了上一个连接,于是DBCP将请求1对应的connection给请求2使用。请求2还没开始使用,JVM切换线程回到请求1 
(3) 在请求1的finally代码块中,又一次close了该connection,此时JVM调度线程回到请求2 
(4) 请求2开始使用该connection,发生异常。 

解决方法很简单,就是在正常代码中close connection之后随即将该connection设成null,然后在finally代码块中首先判断connection是否为null,不为null再close。加了这些判断之后,就避免了某个线程误close connection的现象发生。 

以下是文档给出的示例代码: 

CODE: SELECT ALL
  Connection conn = null;
  Statement stmt = null;  // Or PreparedStatement if needed
  ResultSet rs = null;
  try {
    conn = ... get connection from connection pool ...
    stmt = conn.createStatement("select ...");
    rs = stmt.executeQuery();
    ... iterate through the result set ...
    rs.close();
    rs = null;
    stmt.close();
    stmt = null;
    conn.close(); // Return to connection pool
    conn = null;  // Make sure we don't close it twice
  } catch (SQLException e) {
    ... deal with errors ...
  } finally {
    // Always make sure result sets and statements are closed,
    // and the connection is returned to the pool
    if (rs != null) {
      try { rs.close(); } catch (SQLException e) { ; }
      rs = null;
    }
    if (stmt != null) {
      try { stmt.close(); } catch (SQLException e) { ; }
      stmt = null;
    }
    if (conn != null) {
      try { conn.close(); } catch (SQLException e) { ; }
      conn = null;
    }
  }
原文地址:https://www.cnblogs.com/super119/p/1933323.html