依赖冲突解决的一个思路

问题现象:

本机使用jdk1.8(windows版本)正常运行,远程服务器使用jdk1.8(linux版本)报错了NullPointerException,使用依赖bcprov-jdk15on-1.66;bcprov-jdk14on-1.38;bcprov-ext-jdk15on-1.45;bcprov-jdk15on-1.57;bcprov-jdk15on-1.59;bcpkix-jdk15on-1.66

过程回顾

阶段一

单纯从报错现象看,像是找不到某个类而报了NP空指针,猜测是远程服务器缺少本地的某项依赖,这里需要回顾一下java的类加载机制:
java类加载类顺序
比较发现本地jdk的jre/lib/ext下比远程服务器的jre/lib/ext下多了一些依赖,由于它们也是跟安全类相关的依赖(笔者是在一个数据安全类相关的项目中遇到的该问题),所以猜测可能是缺少了这些依赖导致问题,于是在maven中补充,但是加上之后测试发现问题仍没有解决。

阶段二

注意到有相同依赖不同版本的情况,考虑依赖冲突,在本地进行删除冗余版本依赖测试,此时使用依赖精简为bcprov-jdk15on-1.66;bcprov-jdk14on-1.38;bcprov-ext-jdk15on-1.45;bcpkix-jdk15on-1.66,问题仍没有解决。

阶段三

  1. 遇事不决,堆栈解决,远程调试打出远程服务器的报错堆栈如下:
java.lang.NullPointerException: null 
at com.ibm.crypto.provider.ECUtils.getPrimeParameterSpec(Unknown Source) ~[ibmjceprovider.jar:8.0 build_20160808] 
at com.ibm.crypto.provider.ECParameters.engineInit(Unknown Source) ~[ibmjceprovider.jar:8.0 build_20160808] 
at java.security.AlgorithmParameters.init(AlgorithmParameters.java:304) ~[?:1.8.0-internal] 
at com.ibm.security.x509.AlgorithmId.decodeParams(AlgorithmId.java:680) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.AlgorithmId.<init>(AlgorithmId.java:586) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.AlgorithmId.parse(AlgorithmId.java:511) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.AlgorithmId.parse(AlgorithmId.java:458) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.X509Key.parse(X509Key.java:187) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.X509Key.parse(X509Key.java:215) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.CertificateX509Key.<init>(CertificateX509Key.java:111) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.X509CertInfo.parse(X509CertInfo.java:930) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.X509CertInfo.<init>(X509CertInfo.java:240) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.X509CertInfo.<init>(X509CertInfo.java:226) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.X509CertImpl.parse(X509CertImpl.java:2663) ~[?:8.0 build_20160428] 
at com.ibm.security.x509.X509CertImpl.<init>(X509CertImpl.java:225) ~[?:8.0 build_20160428] at com.ibm.security.x509.X509CertImpl.<init>(X509CertImpl.java:211) ~[?:8.0 build_20160428] 
at java.security.cert.X509CertSelector.match(X509CertSelector.java:2102) ~[?:?] 
at org.bouncycastle.jce.provider.CertStoreCollectionSpi.engineGetCertificates(Unknown Source) ~[bcprov-jdk14-1.38.jar:1.38.0] 
at java.security.cert.CertStore.getCertificates(CertStore.java:145) ~[?:?] at org.bouncycastle.jce.provider.CertPathValidatorUtilities.findCertificates(Unknown Source) ~[bcprov-jdk14-1.38.jar:1.38.0] 
at org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi.engineBuild(Unknown Source) ~[bcprov-jdk14-1.38.jar:1.38.0] 
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) ~[?:?] 
at com.icbc.boom.is.omnichannel.utils.CertUtil.verifyCertificateChainBySM2(CertUtil.java:578) [classes/:?] 
at com.icbc.boom.is.omnichannel.utils.CertUtil.verifyCertificateBySM2(CertUtil.java:630) [classes/:?]
  1. 比较在错误发生情况下本地和远程的不同,可以发现底层都是一些jdk下的包(虽然在分析过程中一度认为是jdk差异导致的问题),暂时不管;注意到此时报错是由于bcprov-jdk14on-1.38导致的【at org.bouncycastle.jce.provider.CertStoreCollectionSpi.engineGetCertificates(Unknown Source) ~[bcprov-jdk14-1.38.jar:1.38.0]】,删除bcprov-jdk14on-1.38,测试发现仍有问题。

阶段四

  1. 使用MyClass.getClass().getProtectionDomain()【myClass为目标类的类全路径】看在正常情况下开始报错的类使用的bcprov-jdk15on-1.66,说明使用该依赖是正确的;
  2. 在本地删除bcprov-jdk15on-1.66发现能够复现问题,但是堆栈中显示有问题的是bcprov-ext-jdk15on-1.45,到此整个问题原因基本就清晰了。

分析结果

  1. 解决:
    删除bcprov-jdk14on-1.38和bcprov-ext-jdk15on-1.45,当然其他冗余版本在之前就已经删除,保留bcprov-jdk15on-1.66
  2. 原因:
    同样的依赖本地运行程序没有问题,而远程服务器运行的时候出现了NullPointerException问题,整个问题分析下来,是因为本地使用了正确的依赖中的类,而远程服务器使用了错误依赖中的类,由于它们的类全路径在不同依赖中是一致的,实际使用哪个取决于JVM加载了哪个。
    ps: 本地JDK使用的HotSpot虚拟机,远程服务器使用的J9虚拟机。

分析总结:

过程总结

可以看到阶段一和阶段二偏向于非理性的分析,当然,在目的不是很明确的情况下,堆栈也不能说直接看出问题所在,但是在平时分析问题的过程中,还是需要多结合报错堆栈分析问题。

依赖冲突问题总结

所以正确的问题排查姿势:
如果存在两个以上类似功能的依赖,不一定需要报错classNotFound,大胆推测是依赖冲突导致。

步骤一

打出报错堆栈,定位到出现问题的类,查看该类是否在多个依赖中有相同的类全路径,进一步确定是依赖冲突问题。

步骤二

报错堆栈中可以看到出现问题的类的所在依赖,删除该依赖;如果你像笔者遇到的问题一样,有可以运行正常的情况,在正常情况下使用-类全路径.getClass().getProtectionDomain()查看正常运行时出现错误的类是由哪个依赖加载出来的,从而定位这个依赖是可以使用的,其他依赖删除。

引用[1]:由于spark,flink代码,只需要实现数据的读入转化为相应的数据结构,就可以通过spark-sql,flink-sql进行多源的数据处理。我们之前的做法是直接通过spark,flinkjar 包的方式,通过传入一个json路径,实现的同步。 但是datax的插件的开发,是reader,writer接口独立开发的,而且数据也是一条条处理的,那么spark肯定不行了。flink的话,需要初始化flink环境,再去实现一个从接口拿去数据的sink,最后souce到hive上,感觉太过于笨重了。基于flume hcatalog(推荐) 。[1] 引用[2]:将hdfs的文件导入hive,datax实现了hdfs的写入,只需要加一个hdfs导入hive就ok了,这个时候通过jdbc连接hive,执行load命令就ok了。 需要修改源码的地方,datax在写入hdfs的时候,必须要一个存在的hdfs目录。指定的分区路径如果不存在就需要手工创建一个。在task实现完毕后,通过jdbcload进hive。[2] 引用[3]:公司要搭建数据中台,离线数据采集就是其中重要一环,目前是基于alibaba的datax组件来搞得。datax的优势,支持多源数据库之间相互同步,日志信息非常清楚,方便定位错误。而且基于datax二次开发也比较简单(实现job,task接口)。 目前的缺点,对于hive的支持力度没那么大,目前官方只实现了hdfs的读写。对于如何基于datax写入hive本文做了一些自己尝试的方法。 解决思路 基于spark,flink框架 。[3] 问题:flink jar包冲突 回答: 当使用flink时,可能会遇到flink jar包冲突的问题。解决这个问题的一种方法是通过排除冲突的jar包来解决。可以在pom.xml文件中指定排除冲突的jar包的groupId和artifactId,以确保只使用所需的jar包。另一种方法是使用dependencyManagement来管理jar包的版本,以避免冲突。这样可以确保所有的jar包都使用相同的版本。另外,还可以使用maven的dependency tree命令来查看项目中所有的依赖关系,以便更好地解决jar包冲突的问题。[1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值