您的位置:首页 > 运维架构 > Tomcat

Tomcat 5.5.23 文档阅读Tips 8 - JNDI Datasource

2011-01-11 22:31 381 查看
本文特别对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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: