跨多个元素共享公共库的单个类加载器是否安全<Host>?



这是我的服务器.xml Tomcat文件中的一个片段:

<Host name="client9001.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
<Host name="client9002.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
<Host name="client9003.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
...
<Host name="client9254.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>

众所周知,每个Host元素都会创建自己的WebappClassloader实例,这使得每个主机都是完全隔离和安全的。通过此设置,我可以安全地使用一个 Tomcat 实例为 X 客户提供服务,每个客户都在不同的子域下提供服务。此设置可防止客户 A 的对象被客户 B 的操作操作。

但是,这会产生对非堆空间的不可扩展需求(因为每个类都加载了 X 次),我想知道我是否可以为所有主机创建一个共享类加载器。我知道 Tomcat 支持这种事情(使用 https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html#Class_Loader_Definitions 中的"Common"类加载器),但我的问题是这样做是否安全。我的意思是,我可以通过消除所有静态字段来使自己的类安全,但这足够了吗?我担心第三方库,如Spring Framework,Log4J,Joda-time和我的产品代码附带的其他几个库。

我已经使用反射来识别这些类中的所有静态变量,并且我发现了 252 个静态和非最终字段以及 5772 个静态和最终字段,所有这些都在第三方代码中(我自己的代码只有 3 个我将消除的字段)。

我应该沿着这条路走下去,查看所有这些 6027 字段吗?我的意思是,这整个想法是一个坏主意吗?如果是这样,那么什么样的代码可以在"公共"类加载器中共享?

是否有任何强有力的证据表明所有这些著名的框架都可以像这样安全地共享,或者我应该将所有内容视为不安全的,直到证明并非如此?

我会非常谨慎...并决定性能是否值得冒险

我在想假设的情况,您可能会在使用共享类加载器时产生一些意想不到的后果......

我看了一下Log4J它通过LogManager创建记录器,它扩展了java.util.logging.LogManager用于维护一组关于记录器和日志服务的共享状态。

想象一下,这些客户端private static final Logger LOGGER = LogManager.getLogger();为每个主机创建记录器的情况

<Host name="client9001.example.com">  
<Host name="client9002.example.com">
<Host name="client9003.example.com"> 
<Host name="BADclient.example.com"> 

由于 LogManager 在所有主机之间静态共享,因此错误的客户端可以调用LogManagers.getLoggerNames();,这会将所有已注册的记录器的列表返回到该静态 LogManager 实例,并会泄漏有关在同一 tomcat 实例上运行的其他客户端的信息/能够删除使用该类的其他主机的事件侦听器(**我还没有尝试过这个**)

基本上,您希望在JVM上运行的所有主机(以及所有未来的主机)之间放置100%共享的代码。常见示例是初始化 JDBC 连接。

同样在代码维护方面,如果您要进入兔子洞,并尝试查看所有静态方法/字段..随着第三方导入的每次更新,您将不得不再次检查它们,以确保没有引入任何内容......甚至可能审查第三方进口的进口

可能会发现这个问题引起了Tomcat类加载器违反委派策略,因为他询问了在Tomcat中使用公共类加载器的危险

最新更新