““markdown
# 记一次 Druid 连接 MySQL 报 Communications link failure 的排查过程
## 问题现象
Druid 连接池启动时报错:
“`
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago.
The driver has not received any packets from the server.
at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:361)
…
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol
(protocol is disabled or cipher suites are in
“`
连接地址是 `jdbc:mysql://127.0.0.1:3306/k_k`,MySQL 就在本机。
## 排查过程
### 1. 先排除基础问题
确认 MySQL 服务在运行、端口 3306 可通,排除服务没启动、防火墙拦截等常见原因。
### 2. 定位到 SSL 握手阶段
堆栈里最关键的一行是:
“`
at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:361)
“`
说明 TCP 连接已经建立,问题出在 **SSL 握手阶段**,而不是网络不通。
### 3. 检查 MySQL 的 TLS 配置
“`sql
SHOW VARIABLES LIKE ‘tls_version’;
“`
结果:
“`
TLSv1,TLSv1.1,TLSv1.2
“`
MySQL 支持 TLSv1.2,但同时也启用了已被淘汰的 TLSv1 和 TLSv1.1。
### 4. 获取 CA 证书
MySQL 启用 SSL 后,数据目录下会生成一组证书文件。先找到数据目录:
“`sql
SHOW VARIABLES LIKE ‘datadir’;
“`
在数据目录里找到 `ca.pem`,这就是服务端的 CA 证书。
### 5. 将 CA 证书导入 Java 信任库
使用 JDK 自带的 `keytool`:
“`bash
/www/server/java/jdk1.8.0_371/bin/keytool -importcert \
-alias MySQLCACert \
-file /www/server/data/ca.pem \
-keystore truststore.jks \
-storepass 你的密码
“`
生成的 `truststore.jks` 位于执行命令时的当前目录。
### 6. 在 JDBC URL 中配置信任库
“`
jdbc:mysql://127.0.0.1:3306/k_k?sslMode=VERIFY_CA&trustCertificateKeyStoreUrl=file:/路径/truststore.jks&trustCertificateKeyStorePassword=你的密码
“`
重新启动后,**仍然报同样的错**。
## 真正的原因
这是 **MySQL Connector/J 8.0.18 及更早版本的一个已知设计问题**:
– Connector/J 8.0.16 出于兼容旧版 MySQL(yaSSL 编译)的考虑,**默认不启用 TLSv1.2**,只尝试用 TLSv1/TLSv1.1 协商。
– JDK 8u371 默认**禁用了 TLSv1 和 TLSv1.1** 这两个不安全的旧协议。
双方在“能用哪个协议”上谈不拢,SSL 握手直接失败。
## 解决方案
在 JDBC URL 中显式指定 TLS 协议版本:
“`
jdbc:mysql://127.0.0.1:3306/k_k?sslMode=VERIFY_CA&trustCertificateKeyStoreUrl=file:/路径/truststore.jks&trustCertificateKeyStorePassword=你的密码&enabledTLSProtocols=TLSv1.2
“`
关键是 `enabledTLSProtocols=TLSv1.2`,它强制驱动使用 TLSv1.2,打破协议僵局。加上后连接正常。
## 几个容易踩的坑
– **`ssl_cipher` 查询为空是正常的**,MySQL 8.0 的密码套件由底层 OpenSSL 管理,不再通过该变量显式列出。
– **`ssl_ca` 只返回 `ca.pem` 而非完整路径也是正常的**,该变量存储的是配置文件里填写的值,要拿绝对路径得查 `datadir`。
– **`keytool` 命令找不到**,是因为它不在系统 PATH 里,需要用 JDK `bin` 目录下的绝对路径执行。
## 后续可以改进的地方
1. **升级驱动**:Connector/J 8.0.19 及以上版本已修复默认不启用 TLSv1.2 的问题,升级后可以去掉 `enabledTLSProtocols` 参数。
2. **清理 MySQL 旧协议**:在 `my.cnf` 的 `[mysqld]` 段加上 `tls_version=TLSv1.2,TLSv1.3`,重启 MySQL,让服务端也彻底禁用旧协议,安全性更好。
## 最终可用配置
“`
jdbc:mysql://127.0.0.1:3306/k_k?sslMode=VERIFY_CA&trustCertificateKeyStoreUrl=file:/路径/truststore.jks&trustCertificateKeyStorePassword=你的密码&enabledTLSProtocols=TLSv1.2
“`
**版本信息**:
– MySQL Connector/J 8.0.16
– Druid 1.1.18
– JDK 1.8.0_371
““
还没有评论,登录后来说两句。