記一次升級(jí)jenkins2.359引發(fā)的項(xiàng)目無(wú)法啟動(dòng)的問(wèn)題

0x00 問(wèn)題出現(xiàn)

同事私聊我一個(gè)文件,并指出測(cè)試環(huán)境的項(xiàng)目無(wú)法啟動(dòng),測(cè)試同事被卡住流程了,需要盡快解決。我拿到文件,看到導(dǎo)致報(bào)錯(cuò)的是java.lang.NoClassDefFoundError: Could not initialize class org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager 。
報(bào)錯(cuò)截圖.png

0x01 分析

思路一:

  1. 看到NoClassDefFoundError,且近期沒(méi)有對(duì)相關(guān)代碼做處理,那么自然想到的就是包版本不一致導(dǎo)致的類的初始化流程報(bào)錯(cuò)。隨后就看了pom文件的依賴,發(fā)現(xiàn)依賴的特別干凈,使用的是org.hibernate.validator:hibernate-validator:6.0.14.Final,沒(méi)有多版本的歧義。
  2. 隨后分析ValueExtractorManager的代碼,發(fā)現(xiàn)代碼中涉及到可能會(huì)出現(xiàn)問(wèn)題的地方只有isJavaFxInClasspath()方法,這里埋一個(gè)點(diǎn)。不過(guò)因?yàn)閷?duì)fx不是很熟,沒(méi)有繼續(xù)探究。
    ValueExtractorManager靜態(tài)代碼塊.png

思路二:

  1. 既然代碼沒(méi)有變動(dòng),那么是否是服務(wù)器環(huán)境變動(dòng)了?想到了之前因?yàn)榭吹絡(luò)enkins中打開(kāi)全是小紅點(diǎn),且存在一部分插件無(wú)法啟動(dòng)的情況,插件目前的版本都依賴高版本的jenkins,并且插件之間依賴又很多,不想自己一個(gè)一個(gè)修復(fù),所以打算直接升級(jí)jenkins。但是最新版的jenkins(當(dāng)時(shí)是2.3.59)依賴java11的環(huán)境。所以在服務(wù)器上安裝了java11的環(huán)境。不想破壞服務(wù)器上其他的應(yīng)用,所以沒(méi)有配置全局的java11環(huán)境,只是啟動(dòng)jenkins使用java11的java_home啟動(dòng)。jenkins啟動(dòng)成功。大體看了一下,沒(méi)有問(wèn)題,并且出現(xiàn)異常的插件也確實(shí)ok了。
  2. bing了一下,大家都說(shuō)這個(gè)問(wèn)題可能是沒(méi)有使用正確的jdk,所以我查看了項(xiàng)目啟動(dòng)命令,確實(shí)使用的是1.8的環(huán)境。
    運(yùn)行時(shí)命令
  3. 為了控制變量,目前就使用本地打包上傳服務(wù)器的方式部署,不通過(guò)jenkins打包了。結(jié)論是,項(xiàng)目啟動(dòng)正常。這樣就基本確認(rèn)了是打包導(dǎo)致的問(wèn)題了。

思路三:

  1. 明確了是打包的問(wèn)題以后,且自己安裝了高版本jdk的環(huán)境后,開(kāi)始排查jenkins中對(duì)maven的配置。
  2. 首先試探性的直接在jenkins的workspace中運(yùn)行mvn命令打包,發(fā)現(xiàn)包可以正常啟動(dòng)。更加確認(rèn)是maven的問(wèn)題。
  3. 隨后檢查了jenkins中maven的配置,確認(rèn)了jenkins中使用的是jdk1.8
    jenkins參數(shù)
  4. 檢查編譯log中的信息,找到了[JENKINS-18403][JENKINS-28294] JDK 'jdk1.8' not supported to run Maven projects.
    編譯信息
  • 搜索第一個(gè)紅框中的內(nèi)容,具體就不多闡述了,直接貼出搜索結(jié)果。隨后直接在maven的global參數(shù)中增加-Dmaven.compiler.release=8參數(shù),增加以后出現(xiàn)Java--Error:java: 無(wú)效的標(biāo)記: -release,沒(méi)有多想,直接去掉了,其實(shí)這個(gè)報(bào)錯(cuò)恰好說(shuō)明了是使用的jdk1.8來(lái)編譯的。
  • 搜索第二框中的內(nèi)容,了解到了maven的toolchains功能,在maven的conf中本身就有toolchains.xml,修改了該文件
<toolchain>
    <type>jdk</type>
    <provides>
      <version>1.8</version>
      <vendor>openjdk</vendor>
    </provides>
    <configuration>
      <jdkHome>/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64</jdkHome>
    </configuration>
  </toolchain>

同樣在項(xiàng)目中增加了toolchain的插件

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-toolchains-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <goals>
                <goal>toolchain</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <toolchains>
            <jdk>
                <version>1.8</version>
                <vendor>openjdk</vendor>
            </jdk>
        </toolchains>
    </configuration>
</plugin>

好,提交,構(gòu)建!?。?????依然無(wú)法啟動(dòng)??。查看日志,發(fā)現(xiàn)新的報(bào)錯(cuò)Toolchain is ignored,不展開(kāi)了,有興趣的同學(xué)查看相關(guān)的材料 ,而且加了這個(gè)以后,本地編譯的時(shí)候會(huì)告訴你找不到1.8的openjdk,因?yàn)闆](méi)有在本地的toolchains.xml配置。原因是因?yàn)閖enkins不是直接運(yùn)行的mvn命令,而是通過(guò)java來(lái)運(yùn)行的,這部分看 編譯信息 圖片的劃線處。

/usr/lib/jvm/java-11-openjdk-11.0.15.0.9-2.el7_9.x86_64/bin/java -cp /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven35-agent-1.13.jar:/usr/share/apache-maven/boot/plexus-classworlds-2.5.2.jar:/usr/share/apache-maven/conf/logging jenkins.maven3.agent.Maven35Main /usr/share/apache-maven/ /var/cache/jenkins/war/WEB-INF/lib/remoting-3028.va_a_436db_35078.jar /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven35-interceptor-1.13.jar /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven3-interceptor-commons-1.13.jar 39141
  1. 將上面的操作全部恢復(fù)??????

思路四:

  1. 輾轉(zhuǎn)反側(cè),還是打算使用最直接的方式--比對(duì)工具,來(lái)進(jìn)行包的比對(duì)。用直接mvn打包出來(lái)的版本和jenkins構(gòu)建的版本進(jìn)行比較。
  2. 下圖是比對(duì)的結(jié)果,??是問(wèn)題包,??是正常包


    比對(duì)結(jié)果

    圖中出現(xiàn)了2中不一致的情況,第一個(gè)框標(biāo)注的是maven打包過(guò)程中產(chǎn)生的信息文件,主要是??的jdk版本和打包時(shí)間的不一致。第二個(gè)框是問(wèn)題包多出來(lái)的幾個(gè)jar?;氐剿悸芬豢ㄗ〉牡胤剑坪醢l(fā)現(xiàn)了一些蹊蹺。

  3. 隨后將本地的jdk版本也換成11,打包,同樣出現(xiàn)了這幾個(gè)包,所以應(yīng)該是包依賴過(guò)程中通過(guò)jdk環(huán)境做的動(dòng)態(tài)打包。通過(guò)idea的MavenHelper插件查詢到這幾個(gè)包是被org.bytedeco:javacv:1.5.3依賴的,隨后就排除了這個(gè)依賴。
    排除依賴
  4. 打包,項(xiàng)目啟動(dòng),It works!??

0x02 拓展

  1. 剛開(kāi)始遇到的問(wèn)題是NoClassDefFoundError,很多同學(xué)也不清楚這個(gè)error和ClassNotFound之間的差異,這里稍微說(shuō)一下
  • ClassNotFound指的是jvm在加載類的過(guò)程中,在classpath中沒(méi)有找到這個(gè)類,注意,這里是沒(méi)有找到。
  • NoClassDefFoundError指的是jvm找到了這個(gè)類,但是在初始化這個(gè)類的時(shí)候,報(bào)錯(cuò)了。雖然日志沒(méi)有輸出具體遇到了什么錯(cuò)誤,但是很明確的就是裝載類出錯(cuò)了。一般來(lái)說(shuō)就是包沖突引起的,也確實(shí)沒(méi)有想到環(huán)境引起的情況。
  • 同樣的問(wèn)題類似NoSuchMethodException,這種也大概率是包沖突引起的
  • maven打包的時(shí)候采用就近原則依賴jar包,不確定的包最好還是顯現(xiàn)的排除掉,或者顯現(xiàn)的定義好版本。
  1. maven中profile標(biāo)簽的activation可以設(shè)置除了jdk外的其他內(nèi)容,有需要的可以查看官方文檔。

0x03 結(jié)論

沒(méi)有想到最后是因?yàn)橐蕾嚢膯?wèn)題,另外,maven采用的是3.5.2的版本,不知道在新版本中activation的實(shí)現(xiàn)方式是否會(huì)關(guān)注maven.compiler.sourcemaven.compiler.targe的內(nèi)容。
中間走了很多彎路,從思路一方向正確的話可以直接到思路四,中間都是自己摸索的過(guò)程。一個(gè)簡(jiǎn)單的問(wèn)題,可能定位起來(lái)會(huì)很復(fù)雜??此祈樌沓烧碌谋澈笠捕际擒浖?duì)默認(rèn)配置的一系列規(guī)劃和思考,通過(guò)這種方式摸索其中的來(lái)龍去脈,也應(yīng)該去思考軟件開(kāi)發(fā)過(guò)程中對(duì)各種環(huán)境的支持范圍,而不是頭疼醫(yī)??,腳疼醫(yī)??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容