最近版本中,某个系统出现启动报错,把编译方式切回原来的maven仓库,启动可以正常。
关键报错如下:
Caused by: java.lang.NoClassDefFoundError: oracle.simplefan.FanManager
at oracle.jdbc.driver.HAManager.configure(HAManager.java:140)
at oracle.jdbc.driver.HAManager.<init>(HAManager.java:122)
at oracle.jdbc.driver.HAManager.getInstance(HAManager.java:126)
at oracle.jdbc.driver.HAManager.enableHAIfNecessary(HAManager.java:725)
at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:713)
at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:39)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:691)
at oracle.jdbc.pool.OracleDataSource.getPhysicalConnection(OracleDataSource.java:384)
at oracle.jdbc.xa.client.OracleXADataSource.getPooledConnection(OracleXADataSource.java:507)
at oracle.jdbc.xa.client.OracleXADataSource.getXAConnection(OracleXADataSource.java:146)
at oracle.jdbc.xa.client.OracleXADataSource.getXAConnection(OracleXADataSource.java:103)
at com.ibm.ws.rsadapter.spi.InternalGenericDataStoreHelper$1.run(InternalGenericDataStoreHelper.java:1370)
at com.ibm.ws.security.util.AccessController.doPrivileged(AccessController.java:118)
at com.ibm.ws.rsadapter.spi.InternalGenericDataStoreHelper.getPooledConnection(InternalGenericDataStoreHelper.java:1389)
at com.ibm.ws.rsadapter.spi.WSRdbDataSource.getPooledConnection(WSRdbDataSource.java:2154)
at com.ibm.ws.rsadapter.spi.WSManagedConnectionFactoryImpl.getConnection(WSManagedConnectionFactoryImpl.java:1830)
at com.ibm.ws.rsadapter.spi.WSManagedConnectionFactoryImpl.createManagedConnection(WSManagedConnectionFactoryImpl.java:1587)
at com.ibm.ws.rsadapter.spi.WSManagedConnectionFactoryImpl.createManagedConnection(WSManagedConnectionFactoryImpl.java:1137)
at com.ibm.ejs.j2c.FreePool.createManagedConnectionWithMCWrapper(FreePool.java:2173)
at com.ibm.ejs.j2c.FreePool.createOrWaitForConnection(FreePool.java:1843)
at com.ibm.ejs.j2c.PoolManager.reserve(PoolManager.java:3869)
at com.ibm.ejs.j2c.PoolManager.reserve(PoolManager.java:3117)
at com.ibm.ejs.j2c.ConnectionManager.allocateMCWrapper(ConnectionManager.java:1556)
at com.ibm.ejs.j2c.ConnectionManager.allocateConnection(ConnectionManager.java:1035)
at com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource.getConnection(WSJdbcDataSource.java:646)
at com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource.getConnection(WSJdbcDataSource.java:613)
...
Caused by: java.lang.ClassNotFoundException: oracle.simplefan.FanManager
at java.net.URLClassLoader.findClass(URLClassLoader.java:610)
at com.ibm.ws.bootstrap.ExtClassLoader.findClass(ExtClassLoader.java:244)
at java.lang.ClassLoader.loadClassHelper(ClassLoader.java:937)
at java.lang.ClassLoader.loadClass(ClassLoader.java:882)
at com.ibm.ws.bootstrap.ExtClassLoader.loadClass(ExtClassLoader.java:135)
at java.lang.ClassLoader.loadClass(ClassLoader.java:865)
... 124 more
从上面的堆栈上看看,没有找到oracle.simplefan.FanManager类,但明显我们是有这么一个类的,在simplefan.jar里边,它属于用于通过ONS订阅RAC事件的类。所以猜测是类加载路径上存在一些差异。这里可以确定的是通过was的ext类加载器没法找到这个类。
<classloader>
<implementation-class>com.ibm.ws.bootstrap.ExtClassLoader</implementation-class>
<delegation-mode>true</delegation-mode>
...
<path>file:/IBM/WebSphere/.../ojdbc8.jar</path>
</classloader>
通过从was的控制台中的类加载器中导出类加载器及对应jar包列表,可以发现ojdb8.jar也是没有这个类的,但是可以发现它是有oracle.jdbc.driver.HAManager这个类,反编译可以看到它的确有引用oracle.simplefan.FanManager。
回头来看原来包路径,包括com.springsource.oracle.jdbc-11.2.0.4.jar,是没有发现存在oracle.simplefan.FanManager的,也没有oracle.jdbc.driver.HAManager。
一个个看有点晕,先做个表格,把上面涉及到的几个关键类排列组合一下。
共享库ojdbc8.jar | 旧仓 | 新仓 | |
---|---|---|---|
oracle.simplefan.FanManager | 无 | 无 | 有,在simplefan.jar |
oracle.jdbc.driver.HAManager | 有,会引用到FanManager | 无 | 无 |
oracle.jdbc.driver.PhysicalConnection | 有,会引用到HAManager | 有,不引用HAManager | 有,在ojdbc6.jar,不引用HAManager |
由于报错是oracle.simplefan.FanManager没找到,那么可以断定,是走的共享库ojdbc8.jar一直上去才会有这样的现象。
通过打开详细类加载器信息,测试环境是没有出现oracle.jdbc.driver.HAManager的,估计是生产是RAC,有配置多个IP的情况,于是考虑模仿生产配多个IP上去看看,可以看到类可以加载到,但是并没有任何报错。
class load: oracle.simplefan.FanManager from: file:/WebSphere/AppServer/profiles/.../*.war/WEB-INF/lib/simplefan-11.2.0.4.jar
...
class load: oracle.ons.ONS from: file:file:/WebSphere/AppServer/profiles/.../*.war/WEB-INF/lib/ons-11.2.0.4.jar
再看看详细的调用路径,看看类是怎么被加载进去的。备注:用jd-gui反编译的结果不对,下面是的是用idea工具来反编译的。
public static void enableHAIfNecessary(String var0, OracleConnection var1) throws SQLException {
boolean var2 = true;
try {
ClassLoader var3 = Thread.currentThread().getContextClassLoader();
Class.forName("oracle.simplefan.FanManager", false, var3);
Class.forName("oracle.ons.ONS", false, var3);
} catch (Throwable var9) {
var2 = false;
}
if (var2) {
Object var10 = (HAManager)allManagers.get(var0);
if (var10 == null) {
String var4 = System.getProperty("oracle.jdbc.fanONSConfig");
short var5 = var1.getVersionNumber();
String var6 = null;
if (var5 < 11100) {
var2 = false;
} else if (var4 != null && !"".equals(var4)) {
var2 = true;
var6 = var4;
} else {
Properties var7 = var1.getServerSessionInfo();
String var8 = var7.getProperty("AUTH_ONS_CONFIG");
if (var5 >= 12101) {
if (var8 == null) {
var2 = false;
} else {
var2 = true;
var6 = var8;
}
} else {
var2 = false;
}
}
if (var2) {
var10 = getInstance(var6);
} else {
var10 = NoSupportHAManager.getInstance();
}
allManagers.putIfAbsent(var0, var10);
}
((HAManager)var10).addConnection(var1);
}
}
从这里可以看到这个类的确是考虑从上下文类加载器加载的,按新仓的配置应该是可以看到的(所以它能够打出加载信息),另外上面的oracle.ons.ONS在新仓也是有的,在ons.jar里边。所以结果也显示var2为true,和日志显示匹配上。 于是后面的逻辑就走到HAManager去,但是很明显由于当前是在ext类加载器,就会出现找不到FanManager的情况。
那么这样也可以解释旧仓为什么没事,因为它的确没有FanManager,另外ONS也是没有的,所以var2必然为false。
到现在为止,还不能解释为什么测试环境没法重现。尝试已经把共享库的ojdbc版本和连接串都尽量参考生产配置方式,但是依然没有报错。我猜应该是后面一段逻辑把var2又重置为false。
上面代码显示,如果oracle小于11.1不开启,否则有配置oracle.jdbc.fanONSConfig(估计是全局配置)或AUTH_ONS_CONFIG(对12.1.0.1+会话级)则开启。当前应用老早就是12+的了,主要看后面两个参数的情况。
关于FAN事件,根据oracle 12c的文档https://docs.oracle.com/en/database/oracle/oracle-database/12.2/tdprc/release_changes.html#GUID-00E012AC-C108-4CFC-9DA1-843E9CA7A569
--机翻,凑合看看
用于快速应用程序通知 (FAN) 的 JDBC 驱动程序支持
Java 数据库连接 (JDBC) 驱动程序现在支持 Oracle 数据库 RAC FAN 事件,以增强对计划内维护和计划外停机时间的支持:
oracle.jdbc.fanEnabled:在驱动程序中启用或禁用FAN支持的系统属性。如果将 Oracle 通用连接池 (UCP) 用作客户端池,则 UCP 优先。
oracle.jdbc.fanONSConfig:驱动程序用作远程 Oracle 通知服务 (ONS) 配置的连接属性。仅 12c 之前的数据库版本需要此属性。
请参阅配置应用程序客户端以实现高可用性。
可能还必须是真正的RAC环境才有相关属性。嗯,我猜只要手工指定一下参数,可能就会重现该报错问题。
考虑了以下场景,用生产的oracle驱动包,用新仓库打出来的包,用多ip配置,server加个参数-Doracle.jdbc.fanONSConfig=10.10.10.10(只能模拟这个,另外一个不知道怎么模拟,且估计生产走下面另一个分支的可能性比较大),重启果然是报错的。尝试把simplefan.jar删掉,发现就可以正常。
后续项目要关注以下情况: