以下是Tomcat中我的server.xml文件的片段:
<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客户提供服务,每个客户都在不同的子域下提供服务。此设置可防止客户B的操作操纵客户A的对象。
但是,这会产生对非堆空间的不可扩展需求(因为每个类都加载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个领域吗?我的意思是,这整个想法是个坏主意吗?如果是这样,那么在“Common”类加载器中可以共享什么样的代码?
有没有强有力的证据证明所有这些着名的框架都可以安全地分享,或者我应该把所有东西都视为不安全 - 直到证明 - 否则?
我会非常谨慎......并决定性能是否值得冒风险
我在考虑假设的情况,你可能会遇到使用共享ClassLoader的一些意想不到的后果......
我看了一下Log4J
,它通过LogManager
创建了记录器,它扩展了java.util.logging.LogManager
,用于维护一组关于Loggers和日志服务的共享状态。
想象一下这些客户端执行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实例上运行的其他客户端的信息/能够删除eventlisteners对于使用同一类的其他主机(**我还没试过这个**)
基本上,您希望将在JVM上运行的所有主机(以及所有将来的主机)之间100%共享的代码放在一起。常见的例子是初始化JDBC连接。
另外在代码维护方面,如果您打算走下兔洞,并尝试查看所有静态方法/字段..每次更新第三方导入时,您都必须再次查看它们,确保没有引入任何内容...甚至可能会检查第三方进口的进口情况
可能会发现这个问题引人注目Tomcat classloader violates delegating policy,因为他询问在Tomcat中使用常见类加载器的危险