在(9)中咱們提到了如何讓ESB項目轉換爲Web項目後日志信息能輸出到控制檯和日誌文件,在繼續研究中,我還發現瞭如下一些問題:java
1. 關於jansi-64.dll的問題。web
log4j2-core庫引用了jansi庫,jansi庫用於在控制檯輸出彩色文字,而這個庫正常使用須要jansi的本地dll文件,而這個文件包含在mule_libs/opt/jline-2.7.jar文件中,當Web項目啓動時,會從這個jar文件中解壓出jansi.dll文件,沒必要拷貝一份到Windows/Systems目錄下,可是這個解壓須要tomcat有一個temp目錄。所以要保證log4j2能正常使用,須要在tomcat根目錄下預先創建一個temp目錄,jansi.dll文件解壓後,會在temp目錄下生成一個名爲jansi-64.dll的dll文件,讓每次使用log4j2時能夠直接引用。apache
2.關於Tomcat對共享jar文件的classpath查找問題。bootstrap
若是須要部署在Tomcat上的ESB項目比較多,並且這些ESB項目引用了相同的庫文件時,通常咱們會將這些公共的庫文件拷貝到tomcat的一個公共目錄下(例如shared_lib目錄),這樣須要部署的ESB項目war文件將會很小,便於部署和複製,但這樣作也帶來了一個問題,就是Tomcat的Classpath尋找問題。tomcat
Tomcat在加載一個Web項目時,查找Web項目的ClassPath時,只限於這個Web項目本身的classes目錄和WEB-INF/lib目錄,上文提到的公共共享目錄不在Tomcat尋找的ClassPath中,這樣對日誌輸出帶了了反作用,若是用於輸出日誌的類不在Web項目的classes目錄和WEB-INF/lib目錄下,將不會有這個類的日誌輸出,下面咱們用實例來證實這一點。app
咱們把Web項目中的全部java代碼抽出成一個公共Java模塊文件CommonModule,編譯成jar,讓Web項目引用。spa
若是咱們在生成Web項目時在WEB-INF/lib目錄下包含CommonModule jar包時,日誌和(9)
同樣能正常輸出。可是若是咱們把CommoModule.jar包移出Web項目到tomcat的共享目錄。日誌不能正常輸出,這是由Tomcat的ClassLoader加載機制決定的。日誌
根據Tomcat的ClassLoader加載的庫路徑:code
能夠看出Tomcat默認的ClassLoader並無包含咱們共享的庫目錄,咱們能夠把CommonModule.jar拷貝到Tomcat的lib目錄下,以便Tomcat可以加載這個jar包,更完全的解決方案是引入VirtualWebappLoader.xml
咱們修改conf/context.xml,在Context節點中加入如下代碼:
<Loader className="org.apache.catalina.loader.VirtualWebappLoader" virtualClasspath="${catalina.home}/shared_lib/*.jar"/>
再次啓動Tomcat時CommonModule.jar就能夠被Tomcat加載,日誌能夠正常輸出。
在設置virtual classpath 時發現了log4j-web jar包與tomcat的衝突。若是virutalclasspath路徑中包含log4j-web的jar包時,啓動時會拋出如下異常
java.io.IOException: java.lang.ClassCastException: Cannot cast org.apache.logging.log4j.web.Log4jServletContainerInitializer to javax.servlet.ServletContainerInitializer at org.apache.catalina.startup.WebappServiceLoader.loadServices(WebappServiceLoader.java:206) at org.apache.catalina.startup.WebappServiceLoader.load(WebappServiceLoader.java:158) at org.apache.catalina.startup.ContextConfig.processServletContainerInitializers(ContextConfig.java:1573) at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1279) at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:887) at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:387) at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117) at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5472) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:899) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:875) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652) at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1260) at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:2002) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: java.lang.ClassCastException: Cannot cast org.apache.logging.log4j.web.Log4jServletContainerInitializer to javax.servlet.ServletContainerInitializer
網上查了一下資料,說是由於Tomcat的Class加載順序致使的,修改了context.xml中ClassLoader的類加載順序(delegate設置爲true)
類加載順序從
變成了
這樣啓動Tomcat雖然沒有異常拋出,可是CommonModule類也沒有被加載,日誌也沒有正常輸出,目前調查肯定的問題是log4j-web這個jar包在加載時致使的問題,只要在virutalclasspath中不包含log4j-web的jar包,啓動Tomcat就沒有問題。
此外,在啓動多個ESB改造的Web項目時,還發現一個問題,virutalclasspath裏不能加載太多的jar包,例如mule_libs/mule/*.jar這樣的classpath路徑是不建議的,建議指向具體的jar包,
由於太多不須要的jar包被virutalclasspath加載時,會致使啓動多個Tomcat的Web項目(我實際啓動了20多個項目)時,出現
Error waiting for multi-thread deployment of WAR files to complete
java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Metaspace
這是virutalclasspath加載的jar太多,致使分配的Meta space溢出。實際應用時MaxMetaspaceSize不可能像-Xmx設置的那麼大,因此若是沒有特殊要求的前提下,建議virutalclasspath只加載日誌記錄類所在的jar文件。