深圳做网站的公司排行网站速度优化 js加载
2026/5/14 8:18:10 网站建设 项目流程
深圳做网站的公司排行,网站速度优化 js加载,网站优化的学习,门户网站维护方案文章目录引言问题引入问题分析在druid时在Hikari时问题解决总结引言 大家好#xff01;今天我们一起探讨一下一个在seata 2.5.0版本修复的小bug#xff0c;如标题所言#xff0c;是和数据库连接池有关的驱动加载有关的问题#xff0c;让我们一起来看看吧。 问题引入 在之…文章目录引言问题引入问题分析在druid时在Hikari时问题解决总结引言大家好今天我们一起探讨一下一个在seata 2.5.0版本修复的小bug如标题所言是和数据库连接池有关的驱动加载有关的问题让我们一起来看看吧。问题引入在之前的代码中如果连接池使用的是druid那就完全没有问题如果使用的是Hikari就会报错。示例seata服务端配置如下:server:port:8091spring:application:name:seata-servermain:web-application-type:nonelogging:config:classpath:logback-spring.xmlfile:path:${log.home:${user.home}/logs/seata}seata:config:# support: nacos, consul, apollo, zk, etcd3type:fileregistry:# support: nacos, eureka, redis, zk, consul, etcd3, sofatype:filestore:# support: file 、 db 、 redis 、 raftmode:dbsession:mode:dblock:mode:dbdb:# If use druid, it is OK. If change to hikari, and then threw an error.datasource:hikaridb-type:mysqldriver-class-name:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://localhost:3396/db_seata?useUnicodetruecharacterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseSSLfalseallowPublicKeyRetrievaltrueuser:admin_seatapassword:123456min-conn:10max-conn:100global-table:global_tablebranch-table:branch_tablelock-table:lock_tabledistributed-lock-table:distributed_lockvgroup-table:vgroup_tablequery-limit:1000max-wait:5000druid:time-between-eviction-runs-millis:120000min-evictable-idle-time-millis:300000test-while-idle:truetest-on-borrow:falsekeep-alive:falsehikari:idle-timeout:600000keepalive-time:120000max-lifetime:1800000validation-timeout:5000# server:# service-port: 8091 #If not configured, the default is ${server.port} 1000如果我使用它datasource: hikari则会抛出错误Caused by: java.lang.RuntimeException: Failed to load driver class com.mysql.cj.jdbc.Driver in either of HikariConfig class loader or Thread context classloader从报错很容易看出来就是连接池初始化时无法加载MYSQL的驱动类。问题分析既然issue里面说到使用druid是正常的而使用HikariCP则不行。肯定这两个进行加载驱动时有所不同。在druid时在seata的DruidDataSourceProvider可以看到下面的代码Druid 正确使用了这个类加载器setDriverClassLoader(getDriverClassLoader()),所以druid是正常加载驱动的DruidDataSourcedsnewDruidDataSource();ds.setDriverClassName(getDriverClassName());//这里明显加载了数据员的驱动类ds.setDriverClassLoader(getDriverClassLoader());ds.setUrl(getUrl());ds.setUsername(getUser());ds.setPassword(getPassword());ds.setInitialSize(getMinConn());ds.setMaxActive(getMaxConn());ds.setMinIdle(getMinConn());ds.setMaxWait(getMaxWait());在Hikari时在seata的HikariDataSourceProvider可以看到下面的代码,可以看到并没有加载到驱动 但 HikariConfig 类没有setDriverClassLoader方法所以 Hikari 无法使用专门的类加载器导致 Hikari 使用默认类加载器无法找到 MySQL 驱动HikariConfigconfignewHikariConfig(properties);config.setDriverClassName(getDriverClassName());config.setJdbcUrl(getUrl());config.setUsername(getUser());config.setPassword(getPassword());问题解决经过分析我们可以知道由于Hikari的设计局限性我们并不能将驱动直接注册在config里面难道我们就不能解决这个问题了吗当然不是解决方法总是有的。一般我们要知道HikariCP 在设置driverClassName时会尝试通过以下两种 ClassLoader 加载驱动类HikariConfig 所在的 ClassLoader当前线程的上下文 ClassLoaderThread Context ClassLoader如果两者都找不到com.mysql.cj.jdbc.Driver类就会抛出这个异常清楚这个点之后我们可以尝试解决这个问题。在seata的所有provider的抽象父类中提供了获取目标驱动的方法protectedClassLoadergetDriverClassLoader(){returnDRIVER_LOADERS.getOrDefault(getDriverClassName(),this.getClass().getClassLoader());}我们可以采用一种“保护现场、恢复现场”的典型设计模式在有关多线程场景经常使用。我们先通过抽象父类获得目标驱动与驱动名然后获取当前线程上下文原驱动再将当前线程的驱动设置成我们第一个获取到的目标驱动并显式注册确保类加载发生在正确的 ClassLoader 上下文中最后finally还原线程驱动即可。seata通过显式加载类和注册驱动到DriverManager后续驱动实例都会存在在DriverManagergetConnection(...)就能用这个实例HikariConfigconfignewHikariConfig(properties);// Get the correct class loaderClassLoaderdriverClassLoadergetDriverClassLoader();StringdriverClassNamegetDriverClassName();// Set driver class name in the correct class loader contextClassLoaderoriginalClassLoaderThread.currentThread().getContextClassLoader();try{Thread.currentThread().setContextClassLoader(driverClassLoader);// 1. Explicitly load and register the drivertry{Class?driverClassClass.forName(driverClassName,true,driverClassLoader);Driverdriver(Driver)driverClass.newInstance();DriverManager.registerDriver(newDriverWrapper(driver));}catch(Exceptione){logger.warn(Failed to explicitly register driver {},driverClassName,e);}// 2. Set configurationconfig.setDriverClassName(driverClassName);config.setJdbcUrl(getUrl());config.setUsername(getUser());config.setPassword(getPassword());}finally{Thread.currentThread().setContextClassLoader(originalClassLoader);}总结绕过 HikariCP 的自动类加载机制Seata 不依赖 HikariCP 自己去加载driverClassName而是提前手动加载并注册 Driver。确保类加载发生在正确的 ClassLoader 上下文中通过Thread.currentThread().setContextClassLoader(driverClassLoader)使得后续config.setDriverClassName(...)调用时HikariCP 内部使用的上下文 ClassLoader 正是能加载驱动的那个。DriverWrapper 保证后续 getConnection() 使用正确驱动即使 DriverManager 中有多个 Driver包装后的 Driver 也能确保使用我们显式加载的那个实例避免 ClassLoader 混乱还有其他问题欢迎评论区友好讨论❤️

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询