tomcat下部署多应用加载公共JNI动态库的解决方案
Posted 隔壁黄二狗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tomcat下部署多应用加载公共JNI动态库的解决方案相关的知识,希望对你有一定的参考价值。
有一个项目需求是这样的:一个tomcat实例,监听一个端口来部署多个应用或者监听多个端口部署多个应用。
这些项目都有一个共性,都使用了JNI来完成某些具体的功能,需要加载动态库so,具体的代码如下:
public class JNI {
static {
System.loadLibrary("jni");
}
public native byte[] digest(String data);
}
上面的代码大致意思就是在服务启动的时候JVM会加载libjni.so,然后应用就可以调用digest方法来完成相应的操作并得到结果。
每个应用打包后,分别部署在一个tomcat实例下,webapps、webapps1、webapps2端口分别为8080、8081、8082,tomcat版本为8.5.68
,部署情况大致如下:
启动成功之后,进行功能测试:
访问ip:8080/demo,功能实现正常
访问ip:8081/demo和ip:8082/demo,均提示Native Library jni.so already loaded in another classloader
问题分析
根据错误提示信息,应该是JVM不允许同一个JNI本地库被不同的classloader加载。
根据tomcat的类加载机制,每个应用都有一个WebApp Classloader来加载,所以libjni.so在应用使用时,第一个WebApp Classloader加载成功后,这个库就不能再被其他WebApp Classloader去加载了。
解决思路与方案
既然问题是由于类加载机制引起的,那么我们可以根据tomcat的类加载机制方向去解决。也就是只让一个类加载器去加载jni,并且可以让每个应用共享,问题不就解决了?问题解决就需要先分析一个tomcat的类加载机制。
1.Tomcat 的类加载机制
Tomcat 的类加载机制相对于 Jvm 的类加载机制做了⼀些改变,没有严格的遵从双亲委派机制。
Tomcat 的类加载在 Jvm 的类加载的基础上增加了Common Classloader、Catalina Classloader 、Shared ClassLoader、WebApp Classloader
Common Classloader通⽤类加载器加载Tomcat使⽤以及应⽤通⽤的⼀些类,位于CATALINA_HOME/lib下,
⽐如servlet-api.jar
Catalina ClassLoader ⽤于加载服务器内部可⻅类,这些类应⽤程序不能访问
SharedClassLoader:各个WebAPP共享的类加载器,加载路径下的类库可被所有的Web应用程序共同使用,但是对Tomcat不可见。
WebappClassLoader:各个Webapp私有的类加载器,加载路径下的类库仅仅可被此Web应用程序使用,对Tomcat和其他的Web应用程序都不可见。
2.解决方案
了解了Tomcat 的类加载机制之后,可以看到Common Classloader通⽤类加载器是Tomcat和WebAPP可以共用的,各个应用所共用的JNI本地库交给Common Classloader来加载,这样就满足了只有一个Classloader来加载JNI本地库,并且每个WebAPP都可以使用。
3.验证
有了解决思路,便来验证一下:
1.首先将使用到的native方法抽离,聚合到一个类里,打成jar后提供给每个应用依赖,如果已经有jar就不需要此步骤了,本文中就是将所有native方法放在了JNI.java里了,然后打包成JNI.jar提供给其他应用使用。
2.依赖JNI.jar,我是使用maven进行依赖管理的,依赖方式如下:
<dependency>
<groupId>com.java.demo</groupId>
<artifactId>jni</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
添加<scope>provided</scope>
是为了jni.jar不被打包进应用的WEB-INF/lib里
3.将JNI.jar放入tomcat/lib
目录下
4.分别部署多个应用,然后启动tomcat
5.分别访问ip:8080/demo、ip:8081/demo、ip:8082/demo发现调用JNI部分的功能均已正常
由此问题得以解决。
总结
tomcat打破了JVM的类加载机制(双亲委派),由WebApp Classloader先加载,每个应用都对应一个WebApp Classloader加载器。
多个应用下,如果引入了相同jar,那么就可以放在tomcat/lib
目录下由Common Classloader统一去加载。
以上是关于tomcat下部署多应用加载公共JNI动态库的解决方案的主要内容,如果未能解决你的问题,请参考以下文章