Web App (JSP/Servlet)
│ lookup("java:comp/env/jdbc/MySQL")
▼
Tomcat Container (Naming Service)
│ ResourceRef → ResourceLink → Global Resource
▼
GlobalNamingResources (server.xml)
│ DataSource 配置:Driver、URL、Pool 參數
▼
MySQL Database (Connector/J 驅動)
mysql-connector-java-3.0.9-stable-bin.jar)# 兩處皆需放置(共享庫 + 應用隔離備選)
cp mysql-connector-java-3.0.9-stable-bin.jar $CATALINA_HOME/common/lib/
cp mysql-connector-java-3.0.9-stable-bin.jar $CATALINA_HOME/webapps/your-app/WEB-INF/lib/
差異:common/lib 供所有 Webapp 共享;WEB-INF/lib 應用獨享,版本衝突時優先。
https://localhost:8080/admin/ → 輸入安裝時設置的管理員帳號密碼。| 字段 | 示例值 | 說明 |
|---|---|---|
| JNDI Name | jdbc/MySQL | 全局唯一,應用 lookup 時用 |
| Data Source URL | jdbc:mysql://192.168.0.16:3306/SUBRDB | IP/主機名 + 端口 + 數據庫 |
| JDBC Driver Class | org.gjt.mm.mysql.Driver | Connector/J 3.x 類名;5.1+ 為 com.mysql.jdbc.Driver |
| User Name | root | 數據庫用戶 |
| Password | ****** | 數據庫密碼 |
| Max Active Connections | 4 | 池最大活躍連接 |
| Max Idle Connections | 2 | 池最大閒置連接 |
| Max Wait (ms) | 500 | 獲取連接最長等待 |
| Validation Query | SELECT 1 | 連接有效性檢查(MySQL 推薦) |
<GlobalNamingResources>
<Resource name="jdbc/MySQL"
auth="Container"
type="javax.sql.DataSource"
maxActive="4"
maxIdle="2"
maxWait="500"
username="root"
password="your_password"
driverClassName="org.gjt.mm.mysql.Driver"
url="jdbc:mysql://192.168.0.16:3306/SUBRDB"
validationQuery="SELECT 1"
testOnBorrow="true"
removeAbandoned="true"
removeAbandonedTimeout="60"
logAbandoned="true" />
</GlobalNamingResources>
<resource-ref>
<description>MySQL Connection Pool</description>
<res-ref-name>jdbc/MySQL</res-ref-name> <!-- 必須與 JNDI Name 一致 -->
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
位置:$CATALINA_HOME/conf/Catalina/localhost/your-app.xml 或應用 META-INF/context.xml
<Context ...>
<ResourceLink name="jdbc/mysql"
global="jdbc/MySQL"
type="javax.sql.DataSource" />
</Context>
注意:name 是應用內部 JNDI 名(java:comp/env/jdbc/mysql),global 指向全局資源名。
$CATALINA_HOME/bin/shutdown.sh
$CATALINA_HOME/bin/startup.sh
# 查看 logs/catalina.out 確認無 DataSource 錯誤
<%@ page import="java.sql.*, javax.sql.*, javax.naming.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<html><body>
<h2>MySQL 連接池測試</h2>
<%
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mysql");
Connection conn = ds.getConnection();
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT VERSION() AS ver, NOW() AS now");
if (rs.next()) {
out.println("MySQL 版本: " + rs.getString("ver") + "<br>");
out.println("服務器時間: " + rs.getTimestamp("now") + "<br>");
}
rs.close();
stmt.close();
} finally {
conn.close(); // 歸還池,而非關閉物理連接
}
%>
</body></html>
| 現象 | 原因 | 解決 |
|---|---|---|
NameNotFoundException: jdbc/MySQL | JNDI 名不匹配 | 檢查 web.xml res-ref-name、context.xml name、server.xml Resource name 三者一致 |
ClassNotFoundException: org.gjt.mm.mysql.Driver | 驅動包未放 common/lib | 複製 jar 到 common/lib 並重啟 Tomcat |
Communications link failure | URL 錯誤/防火牆/MySQL bind-address | 檢查 IP、端口、MySQL bind-address=0.0.0.0、安全組 |
| 連接泄漏(池耗盡) | 未在 finally 關閉 Connection | 強制 removeAbandoned="true"、代碼規範 try-with-resources (JDK 7+) |
| 驗證查詢報錯 | Validation Query 不兼容 | MySQL 用 SELECT 1,Oracle 用 SELECT 1 FROM DUAL |
maxActive = (CPU核數 * 2) + 有效磁盤數 經驗公式,再壓測微調。testOnBorrow=true + validationQuery=SELECT 1;高併發可改 testWhileIdle=true + timeBetweenEvictionRunsMillis=30000。removeAbandoned=true + removeAbandonedTimeout=60 + logAbandoned=true。NumActive、NumIdle、MaxWaitTime,接入 Prometheus/Grafana 告警。Tomcat 5 配置 MySQL JNDI 連接池的核心鏈路是:驅動部署 → 全局 Resource 定義 → web.xml resource-ref 聲明 → Context ResourceLink 綁定 → 應用 lookup 使用。掌握這條鏈路,任意數據庫(Oracle、PostgreSQL、SQL Server)只需換驅動、改 URL、調驅動類名即可復用。生產環境務必配合連接池監控、驗證查詢、棄用回收三件套,避免連接泄漏拖垮整個應用集群。