背景
java現代化編程中,通常會使用CI/CD技術進行項目部署,筆者最近的項目就使用了Jekins部署項目到docker環境中。
由於docker image中的jdk版本比較低,計划升級jdk的版本,以規避JDK 1.8.u1xx系列的bug。
舉例來說,筆者將docker文件中的版本升級到1.8u322,修改Dockerfile為如下
FROM openjdk:8u322-jre
然后使用IDEA的Docker插件發布,正常運行。唯獨使用POI導出Excel時候報錯。如下
2022-03-04 10:37:51.471 [XNIO-1 task-1] DEBUG [com.alibaba.excel.context.WriteContextImpl.finish] - Finished write. 2022-03-04 10:37:51.474 [XNIO-1 task-1] DEBUG [org.springframework.web.servlet.FrameworkServlet.logResult] - Failed to complete request: com.alibaba.excel.exception.ExcelGenerateException: java.lang.UnsatisfiedLinkError: /usr/local/openjdk-8/lib/amd64/libawt_xawt.so: libXext.so.6: cannot open shared object file: No such file or directory 2022-03-04 10:37:51.476 [XNIO-1 task-1] ERROR [io.undertow.servlet.api.LoggingExceptionHandler.handleThrowable] - UT005023: Exception handling request to /API/SAPOrder/exportList org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.alibaba.excel.exception.ExcelGenerateException: java.lang.UnsatisfiedLinkError: /usr/local/openjdk-8/lib/amd64/libawt_xawt.so: libXext.so.6: cannot open shared object file: No such file or directory at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:665) ~[javax.servlet-api-4.0.1.jar!/:4.0.1] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:750) ~[javax.servlet-api-4.0.1.jar!/:4.0.1] at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.websockets.jsr.JsrWebSocketFilter.doFilter(JsrWebSocketFilter.java:173) ~[undertow-websockets-jsr-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:97) ~[spring-web-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at com.www.mes.common.filter.HttpTraceLogFilter.doFilterInternal(HttpTraceLogFilter.java:54) ~[classes!/:0.1-SNAPSHOT] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.19.RELEASE.jar!/:5.1.19.RELEASE] at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) ~[undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) ~[undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) ~[undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) ~[undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99) [undertow-servlet-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.server.Connectors.executeRootHandler(Connectors.java:376) [undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830) [undertow-core-2.0.32.Final.jar!/:2.0.32.Final] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_202] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_202] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_202]
當然,筆者的同事也嘗試用Oracle JDK1.8u202在centos7.9中制作Docker Image文件,同樣也是少文件,只是報錯的路徑不一樣。
經過筆者進入docker嘗試,openjdk8u322是在debian11.2上面制作完成的(這里要求了解linux的辨別方法)
解決方法
筆者探索解決方法的過程如下:
首先進入docker鏡像,發現文件/usr/local/openjdk-8/lib/amd64/libawt_xawt.so:是存在的。而后面的libXext.so.6則不存在。
筆者在docker中執行以下命令
whereis libXext.so.6
並沒發現(截至寫博文時已經修復,不再截圖)
后來根據筆者的操作系統知識,看上面的報錯,應該是libawt_xawt.so缺少libXext.so.6的依賴。
在docker中執行以下命令,發現awt依賴的如下(類似下面,筆者已經安裝了libXrender.so情況下執行該命令,如果沒安裝,會有很多顯示not found)
root@0bde6b347fbd:/# ldd /usr/local/openjdk-8/lib/amd64/libawt_xawt.so /bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8) linux-vdso.so.1 (0x00007fffcffcb000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fbf32914000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fbf327d0000) libawt.so => /usr/local/openjdk-8/lib/amd64/libawt.so (0x00007fbf324fc000) libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007fbf324e7000) libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007fbf323a4000) libXrender.so.1 => /usr/lib/x86_64-linux-gnu/libXrender.so.1 (0x00007fbf3219a000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fbf32192000) libXtst.so.6 => not found libXi.so.6 => not found libjava.so => /usr/local/openjdk-8/lib/amd64/libjava.so (0x00007fbf31f68000) libjvm.so => /usr/local/openjdk-8/lib/amd64/server/libjvm.so (0x00007fbf30f51000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbf30d8c000) /lib64/ld-linux-x86-64.so.2 (0x00007fbf32ba0000) libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007fbf30d5f000) libverify.so => /usr/local/openjdk-8/lib/amd64/libverify.so (0x00007fbf30b4f000) libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007fbf30b4a000) libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007fbf30944000) libbsd.so.0 => /usr/lib/x86_64-linux-gnu/libbsd.so.0 (0x00007fbf3092d000) libmd.so.0 => /usr/lib/x86_64-linux-gnu/libmd.so.0 (0x00007fbf3091e000)
筆者在docker中執行以下命令,查詢相關依賴
sed -i "s@http://deb.debian.org@https://mirrors.aliyun.com@g" /etc/apt/sources.list sed -i "s@http://security.debian.org@https://mirrors.aliyun.com@g" /etc/apt/sources.list apt update apt-cache search libxext
你只需安裝 libxext6即可,即執行以下命令
apt-get install libxext6
但是為了兼容不同版本的openjdk8(不同版本的debian查詢得到的libxext可能不一樣,比如libxext5,libxext4),筆者選擇使用以下命令
apt-get install libxext*
當然,也會額外下載其他東西,看你的需要吧。
經過多次測試,筆者得到以下的Dockerfile配置
FROM openjdk:8u322-jre ..... RUN sed -i "s@http://deb.debian.org@https://mirrors.aliyun.com@g" /etc/apt/sources.list \ && sed -i "s@http://security.debian.org@https://mirrors.aliyun.com@g" /etc/apt/sources.list RUN apt update RUN apt-get install libxext* libxrender1* libxtst* -y ....
按照以上配置后,再次用poi導出excel不再報錯,再來執行命令看看libawt_xawt.so依賴
ldd /usr/local/openjdk-8/lib/amd64/libawt_xawt.so
如果你是自己制作鏡像,比如使用centos7.9,執行以下命令
sudo yum install libXext* libXrender* libXtst* -y
有人問,為什么,我在宿主機里面安裝oracle jdk/jre,openjdk/jre很少遇到問題?
答:因為制作docker鏡像時,關於操作系統依賴都是最低依賴庫,以便方便發現(Image體積小90-200M左右,否則數GB怎么發行)。
一般宿主機安裝linux都會安裝libX11或者xorg,即桌面相關,所以不會,會順便安裝上面的依賴庫,所以不會報錯。
或者因為安裝其他庫(liboffice,imageMagick,vnc筆者猜想的),順便安裝了xorg,libx11。
java awt中含有圖像圖形相關的方法/函數,所以需要很多so動態庫,而我們使用的POI很可能依賴圖像運算相關庫,所以報錯了。
時間太晚,筆者沒去研究 libxext libxrender libxtst,感興趣的你可以自己搜索了解一下。