Mule ESB Http项目转换为Tomcat项目(10) 关于日志问题的补充

|▌冷眼眸甩不掉的悲伤 提交于 2019-11-27 18:51:11

在(9)中我们提到了如何让ESB项目转换为Web项目后日志信息能输出到控制台和日志文件,在继续研究中,我还发现了以下一些问题:

1. 关于jansi-64.dll的问题。

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时可以直接引用。

2.关于Tomcat对共享jar文件的classpath查找问题。

    如果需要部署在Tomcat上的ESB项目比较多,而且这些ESB项目引用了相同的库文件时,一般我们会将这些公共的库文件拷贝到tomcat的一个公共目录下(例如shared_lib目录),这样需要部署的ESB项目war文件将会很小,便于部署和复制,但这样做也带来了一个问题,就是Tomcat的Classpath寻找问题。

    Tomcat在加载一个Web项目时,查找Web项目的ClassPath时,只限于这个Web项目自己的classes目录和WEB-INF/lib目录,上文提到的公共共享目录不在Tomcat寻找的ClassPath中,这样对日志输出带了了副作用,如果用于输出日志的类不在Web项目的classes目录和WEB-INF/lib目录下,将不会有这个类的日志输出,下面我们用实例来证明这一点。

   我们把Web项目中的所有java代码抽出成一个公共Java模块文件CommonModule,编译成jar,让Web项目引用。

         如果我们在生成Web项目时在WEB-INF/lib目录下包含CommonModule jar包时,日志和(9)
一样能正常输出。但是如果我们把CommoModule.jar包移出Web项目到tomcat的共享目录。日志不能正常输出,这是由Tomcat的ClassLoader加载机制决定的。

      根据Tomcat的ClassLoader加载的库路径:

  • Bootstrap classes of your JVM(JDK自带的rt.jar)
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application
  • System class loader classes (Tomcat自带的bootstrap.jar、tomcat-juli.jar、commons-deamon.jar)
  • Common class loader classes(包含在Tomcat目录的lib目录下的jar文件)

     可以看出Tomcat默认的ClassLoader并没有包含我们共享的库目录,我们可以把CommonModule.jar拷贝到Tomcat的lib目录下,以便Tomcat能够加载这个jar包,更彻底的解决方案是引入VirtualWebappLoader.

    我们修改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)

类加载顺序从

  • Bootstrap classes of your JVM
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application
  • System class loader classes (described above)
  • Common class loader classes (described above)

变成了     

  • Bootstrap classes of your JVM
  • System class loader classes (described above)
  • Common class loader classes (described above)
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application

     这样启动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文件。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!