web server apache tomcat11-09-JNDI Datasource

前言

整理这个官方翻译的系列,原因是网上大部分的 tomcat 版本比较旧,此版本为 v11 最新的版本。

开源项目

从零手写实现 tomcat minicat 别称【嗅虎】心有猛虎,轻嗅蔷薇。

系列文章

web server apache tomcat11-01-官方文档入门介绍

web server apache tomcat11-02-setup 启动

web server apache tomcat11-03-deploy 如何部署

web server apache tomcat11-04-manager 如何管理?

web server apache tomcat11-06-Host Manager App -- Text Interface

web server apache tomcat11-07-Realm Configuration

web server apache tomcat11-08-JNDI Resources

web server apache tomcat11-09-JNDI Datasource

web server apache tomcat11-10-Class Loader

...

介绍

JNDI 数据源配置在 JNDI-Resources-HOWTO 中有详细说明。然而,来自 tomcat-user 的反馈显示,针对特定配置的具体细节可能会相当棘手。

以下是一些已发布到 tomcat-user 的针对流行数据库的示例配置,以及一些通用数据库使用的一般提示。

您应该注意,由于这些注释是从发布到 tomcat-user 的配置和/或反馈中派生的,因此可能会因人而异 :-)。如果您有任何其他经过测试的配置,您认为可能对更广泛的受众有用,或者如果您认为我们可以以任何方式改进此部分,请告诉我们。

请注意,Tomcat 7.x 和 Tomcat 8.x 之间的 JNDI 资源配置在一定程度上有所变化,因为它们使用了不同版本的 Apache Commons DBCP 库。您很可能需要修改旧的 JNDI 资源配置以匹配下面示例中的语法,以使它们在 Tomcat 11 中正常工作。有关详细信息,请参阅 Tomcat 迁移指南。

此外,请注意,JNDI DataSource 配置一般以及本教程特别假设您已经阅读并理解了 Context 和 Host 配置参考,包括后者参考中关于自动应用程序部署的部分。

DriverManager、服务提供程序机制和内存泄漏

java.sql.DriverManager 支持服务提供程序机制。这个特性是,通过提供 META-INF/services/java.sql.Driver 文件宣布自己的所有可用 JDBC 驱动程序会自动被发现、加载和注册,从而使您无需在创建 JDBC 连接之前显式加载数据库驱动程序。然而,在所有 Java 版本中,该实现在 servlet 容器环境中基本上是有缺陷的。问题在于,java.sql.DriverManager 仅会扫描驱动程序一次。

Apache Tomcat 包含的 JRE 内存泄漏预防监听器通过在 Tomcat 启动期间触发驱动程序扫描来解决这个问题。这是默认启用的。这意味着只有可见于公共类加载器及其父加载器的库将被扫描以寻找数据库驱动程序。这包括 CATALINA_HOME/lib、CATALINA_BASE/lib、类路径和模块路径中的驱动程序。打包在 Web 应用程序中(在 WEB-INF/lib 中)和共享类加载器中(如果已配置)的驱动程序将不可见,并且不会自动加载。如果您考虑禁用此功能,请注意,扫描将由使用 JDBC 的第一个 Web 应用程序触发,导致在重新加载此 Web 应用程序时以及依赖于此功能的其他 Web 应用程序时失败。

因此,在其 WEB-INF/lib 目录中具有数据库驱动程序的 Web 应用程序不能依赖于服务提供程序机制,并且应该显式注册驱动程序。

java.sql.DriverManager 中的驱动程序列表也是已知的内存泄漏源。由 Web 应用程序注册的任何驱动程序在 Web 应用程序停止时必须取消注册。当 Web 应用程序停止时,Tomcat 将尝试自动发现并注销由 Web 应用程序类加载器加载的任何 JDBC 驱动程序。但是,预期应用程序通过 ServletContextListener 自行执行此操作。

数据库连接池(DBCP 2)配置

Apache Tomcat 中默认的数据库连接池实现依赖于 Apache Commons 项目的库。使用以下库:

  • Commons DBCP 2
  • Commons Pool 2

这些库位于一个单独的 JAR 中,路径为 $CATALINA_HOME/lib/tomcat-dbcp.jar。但是,仅包含了用于连接池的类,并且已经重命名了包以避免干扰应用程序。

DBCP 2 支持 JDBC 4.1。

安装

查看 DBCP 2 文档以获取完整的配置参数列表。

预防数据库连接池泄漏

数据库连接池创建和管理到数据库的连接池。重新使用已经存在的连接比打开新连接更高效。

连接池存在一个问题。Web 应用程序必须显式关闭 ResultSet、Statement 和 Connection。如果 Web 应用程序未关闭这些资源,它们可能永远不会再次可用,从而导致数据库连接池“泄漏”。如果没有更多可用的连接,则最终可能会导致您的 Web 应用程序数据库连接失败。

有一个解决方案。Apache Commons DBCP 2 可以配置为跟踪和恢复这些被遗弃的数据库连接。它不仅可以恢复它们,还可以为打开这些资源但从未关闭它们的代码生成堆栈跟踪。

要配置 DBCP 2 DataSource,以便移除和重新使用被遗弃的数据库连接,请在 DBCP 2 DataSource 的 Resource 配置中添加以下一个或两个属性:

  • removeAbandonedOnBorrow=true
  • removeAbandonedOnMaintenance=true

这两个属性的默认值都是 false。请注意,只有当设置 timeBetweenEvictionRunsMillis 为正值时,removeAbandonedOnMaintenance 属性才会生效。

有关这些属性的详细文档,请参阅 DBCP 2 文档。

使用 removeAbandonedTimeout 属性设置数据库连接在被视为被遗弃之前空闲的秒数。

removeAbandonedTimeout="60"

移除被遗弃连接的默认超时时间为 300 秒。

如果希望

DBCP 2 记录已遗弃数据库连接资源的代码的堆栈跟踪,则可以将 logAbandoned 属性设置为 true。

logAbandoned="true"

默认值为 false。

MySQL DBCP 2 示例

0. 介绍

以下是已知可以工作的 MySQL 和 JDBC 驱动程序的版本:

  • MySQL 3.23.47、MySQL 3.23.47 使用 InnoDB、MySQL 3.23.58、MySQL 4.0.1alpha
  • Connector/J 3.0.11-stable(官方 JDBC 驱动程序)
  • mm.mysql 2.0.14(一个旧的第三方 JDBC 驱动程序)

在继续之前,请不要忘记将 JDBC 驱动程序的 jar 复制到 $CATALINA_HOME/lib。

1. MySQL 配置

确保按照以下说明操作,因为变化可能会导致问题。

  • 创建一个新的测试用户、一个新的数据库和一个单个的测试表。您的 MySQL 用户必须分配密码。如果您尝试使用空密码连接,驱动程序将失败。
mysql> GRANT ALL PRIVILEGES ON *.* TO javauser@localhost
    ->   IDENTIFIED BY 'javadude' WITH GRANT OPTION;
mysql> create database javatest;
mysql> use javatest;
mysql> create table testdata (
    ->   id int not null auto_increment primary key,
    ->   foo varchar(25),
    ->   bar int);

测试数据插入

执行完测试后,应该移除上述用户!

接下来,向 testdata 表中插入一些测试数据。

mysql> insert into testdata values(null, 'hello', 12345);
Query OK, 1 row affected (0.00 sec)

mysql> select * from testdata;
+----+-------+-------+
| ID | FOO   | BAR   |
+----+-------+-------+
|  1 | hello | 12345 |
+----+-------+-------+
1 row in set (0.00 sec)

mysql>

上下文配置

在 Tomcat 中配置 JNDI DataSource,通过在 Context 中添加资源声明来实现。

例如:

<Context>

    <!-- maxTotal: 数据库连接池中的最大连接数。确保你配置了足够大的 mysqld max_connections 来处理所有的数据库连接。设置为 -1 表示无限制。 -->

    <!-- maxIdle: 连接池中保留的最大空闲数据库连接数。设置为 -1 表示无限制。请参阅 DBCP 2 文档以了解有关此参数以及 minEvictableIdleTimeMillis 配置参数的更多信息。 -->

    <!-- maxWaitMillis: 等待数据库连接可用的最长时间(以毫秒为单位),例如在此示例中为 10 秒。如果超过此超时时间,则会抛出异常。设置为 -1 表示无限等待。 -->

    <!-- username 和 password: 数据库连接的 MySQL 用户名和密码  -->

    <!-- driverClassName: 旧的 mm.mysql JDBC 驱动程序的类名是 org.gjt.mm.mysql.Driver - 我们建议使用 Connector/J。官方 MySQL Connector/J 驱动程序的类名是 com.mysql.jdbc.Driver。 -->

    <!-- url: 连接到 MySQL 数据库的 JDBC 连接 URL。 -->

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

</Context>

web.xml 配置

现在为此测试应用程序创建一个 WEB-INF/web.xml。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
  version="6.0">
  <description>MySQL Test App</description>
  <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>
</web-app>

测试代码

现在创建一个简单的 test.jsp 页面供以后使用。

<%@ 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>

该 JSP 页面利用了 JSTL 的 SQL 和 Core 标签库。您可以从 Apache Tomcat Taglibs - Standard Tag Library 项目中获取它 — 确保获取 1.1.x 或更新的版本。一旦您获得了 JSTL,将 jstl.jar 和 standard.jar 复制到您的 Web 应用的 WEB-INF/lib 目录中。

最后,将您的 Web 应用部署到 $CATALINA_BASE/webapps,可以是一个名为 DBTest.war 的 war 文件,也可以是一个名为 DBTest 的子目录。

部署后,将浏览器指向 http://localhost:8080/DBTest/test.jsp,即可查看您的辛勤工作的成果。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容