记一次 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 里,需要用 JDKbin目录下的绝对路径执行。
后续可以改进的地方
- 升级驱动 :Connector/J 8.0.19 及以上版本已修复默认不启用 TLSv1.2 的问题,升级后可以去掉
enabledTLSProtocols参数。 - 清理 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