MySQL JDBC Driver源码解读
You can get the source code from Github: https://github.com/mysql/mysql-connector-j
当调用com.mysql.cj.jdbc.Driver类时, 静态代码块会默认加载驱动.
Driver
DriverManager.getConnection时,会通过SPI获取
无需手动注册
ClassLoader加载com.mysql.cj.jdbc.Driver时,会通过静态内部类注册。
也就是说,SPI扫到这个类,就会注册.
设计比较巧妙的一点:
classDiagram NonRegisteringDriver <|-- Driver NonRegisteringDriver: Connection connect(String url, Properties info)
创建Connection
sequenceDiagram participant ClientImpl participant DriverManager participant Driver participant Connection participant NativeSession participant NativeProtocol participant StatementImpl alt 创建连接 ClientImpl ->> DriverManager: getConnection() DriverManager ->> DriverManager: 加载MySQL的Driver DriverManager ->> Driver: connect() Driver ->> Connection:() Connection ->> NativeSession: connect() NativeSession ->> NativeProtocol: connect() end alt 执行查询 ClientImpl ->> Connection: createStatement() Connection ->> StatementImpl: () Connection -->> ClientImpl: Statement ClientImpl ->> StatementImpl: execute() StatementImpl ->> NativeSession: execSQL()获取结果 end alt 查询结果 ClientImpl ->> StatementImpl: getResultSet() StatementImpl -->> ClientImpl: ResultSet end
构建ConnectionUrl
数据库Url和连接参数 的容器
根据schema来区分ConnectionUrl的类型ConnectionUrl.Type, Type中定义了创建不同ConnectionUrl的类名和参数。
构建Connection
Connection代表与特定数据库的会话。在 Connection 的上下文中,执行 SQL 语句并返回结果。
没有Connection持有一个NativeSession
创建新的socket链接 -> 与服务端握手 -> 构建与服务端的通信协议 -> 通过MySQL协议与服务端连接
NativeSession
与SocketServer的会话
NativeSocketConnection
持有与服务端链接的InputStream和OutputStream
SocketFactory
允许在驱动程序中创建可插入套接字的接口
创建Socket链接
com.mysql.cj.protocol.Protocol
协议提供与 MySQL 服务器通信的设施
充斥着好多pocket相关的类
DNS更新后新ip未生效问题
在StandardSocketFactory.connect方法中通过域名获取ip地址。
https://github.com/mysql/mysql-connector-j/blob/805f872a57875f311cb82487efcfb070411a3fa0/src/main/core-impl/java/com/mysql/cj/protocol/StandardSocketFactory.java#L130
1 | InetAddress[] possibleAddresses = InetAddress.getAllByName(this.host); |
1 | // look-up or remove from cache |
#InetAddress 带了缓存
在Java中,
InetAddress类用于处理IP地址,其内部实现了DNS缓存机制以提高域名解析的效率。通过设置JVM参数或者系统属性,可以调整InetAddress的DNS缓存行为。
1. JVM参数设置
可以通过在启动Java应用程序时传递JVM参数来设置DNS缓存的行为。以下是常用的JVM参数:
java -Dnetworkaddress.cache.ttl=10 -jar test.jar:这个参数设置了DNS缓存的时间为10秒。默认情况下,如果不解设,缓存时间为30秒。java -Dnetworkaddress.cache.ttl=0 -jar test.jar:设置为0意味着不缓存解析成功的结果,每次解析都会访问DNS服务器。java -Dnetworkaddress.cache.ttl=-1 -jar test.jar:设置为-1表示永久缓存解析成功的结果,直到应用程序重启。
2. 系统属性设置
除了JVM参数,还可以通过设置系统属性来调整DNS缓存策略:
Security.setProperty("networkaddress.cache.ttl", "-1"):通过编程方式设置DNS缓存时间为永久。
3. 源码级别的设置
在Java代码中,可以通过修改InetAddressCachePolicy类的行为来控制DNS缓存策略。例如,可以通过修改cachePolicy静态变量的值来控制缓存时间。
4. 安全管理器(SecurityManager)的影响
当设置了安全管理器(SecurityManager)时,对于DNS缓存时间的默认行为会有所改变:
- 如果设置了
SecurityManager,默认的CachePolicy为FOREVER,即永久缓存DNS的结果,直到进程终止。 - 如果没有设置
SecurityManager,可以通过修改java.security文件中的相关属性来控制DNS缓存行为。
注意事项
- 永久缓存DNS结果(
FOREVER)可能会导致应用程序无法响应DNS变化,因此在生产环境中应谨慎使用。 - 缓存策略的设置应该根据应用程序的实际需求和网络环境来决定,以确保既高效又安全。
- 在设置DNS缓存策略时,还应考虑无效DNS缓存时间的设置,即当DNS解析失败时的缓存时间,默认情况下这个时间为10秒。
总结
- 创建对象,大量使用反射的原因?
灵活