IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    maven仓库切换后WebSphere启动报oracle错误问题分析

    蔡晓建发表于 2022-04-14 00:00:00
    love 0

    问题描述

    最近版本中,某个系统出现启动报错,把编译方式切回原来的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删掉,发现就可以正常。

    总结汇总

    • 从现在oracle驱动包版本看,目前主要有11版本,12版本,19版本,只有11版本是有jdk6版本的,其他最低得jdk8。各个版本,如果是基于maven依赖管理的话,都是依赖了ons,simplefan等包的。并且从12版本开始,PhysicalConnection代码就会关联到FanManager。
    • 其他一些商业应用容器,例如宝蓝德内部是11版本的(../lib/ojdbc6-11.2.0.3.0.jar),所以不会出现类似问题。
    • 应用目前主要是11版本的,老仓库是不带依赖的(没有ons,simplefan等),新仓库是有带依赖的,这主要是坐标发生了变化。
    • WAS目前看用12版本的,但是不带依赖的,所以在WAS+新仓应用+RAC的情况下,会用到HAManager,但是会加载不到FanManager。

    后续项目要关注以下情况:

    1. 单纯修复方法来说,当前WAS这种情况,建议把新仓库的simplefan排除掉。
    2. 后续升级Oracle客户端的情况需要注意这块东西,例如宝蓝德,可能要考虑在公共依赖中将simplefan统一排掉。


沪ICP备19023445号-2号
友情链接