Pandora的启动优化

pandora3进行了很多重构,上周花了几天时间对启动过程做了一些优化。官方的tomcat只部署一个及简单的servlet应用,在我的机器上启动过程大概在1秒左右,如果应用使用了spring-mvc框架,启动过程大约在2000-2400毫秒左右。

如果使用Ali-Tomcat,启动过程中要先启动pandora容器,同一个spring-mvc的样例应用,总启动时间在4-5秒左右。优化的手段主要有2点:1) 部分模块异步化加载(前提是其它模块以及应用的启动对该模块没有依赖)。 2) 在自定义的classloader.loadClass方法里针对java7开启并行加载(在我的场景下所加载的类大约1000-2000个,使用4个线程,性能上约有20-30%的提升)。

java7的classloader所支持的并行加载,实质是把原先loadClass方法原先粗粒度的锁synchronized(this)换成了细粒度的锁synchronized (getClassLoadingLock(name)),为每个class都分配一个锁。这部分特性其实可以移植到自定义的Classloader实现里来,使得jdk6上也可以享用并行加载。

自定义的classloader要使用jdk7的并行加载机制,需要在静态构造块里注册为可并行加载:

static {
    ClassLoader.registerAsParallelCapable();
}

注意它要求当前classloader所有的父类都进行注册才行,比如我们在自定义的 MyClassLoader中覆盖了loadClass方法, 假设它的父类是 ClassLoaderBase,而ClassLoaderBase又继承自系统的URLClassLoader,那么要求URLClassLoaderClassLoaderBase 都必须在静态块中有注册声明可并行加载才行(URLClassLoader及其父类在jdk代码中是有注册的)。

另外,使用spring-mvc框架的应用,之所以比普通的servlet启动时慢,很大一部分原因是在于servlet3的web-fragment.xml合并的过程比较耗时。这个过程是在Pandora3启动后,输出应用启动日志过程中体会到的一个明显的“卡顿”:

2014/06/18 10:36:09:588 [INFO] Pandora - Container started. time elapsed: 439 ms
2014-06-18 10:36:10,740 org.springframework.web.servlet.FrameworkServlet initServletBean

这中间居然耗费了将近1.2秒,通过开启日志debug,发现主要的耗时在 WebXml.merge 方法上。原因是servlet3引入了web模块化配置,对原先的web.xml可以由多个“片段”组成,这些片段使用web-fragment.xml来描述,可以对一组servlet/Filter/Listener + web-fragment.xml 打成一个jar,即一个web模块。

这种做法带来一定灵活性,但要求应用在启动时,必须对WEB-INF/lib下的jar,以及其它路径下classloader可访问的jar(这部分可配置,默认包含)进行检查,合并web-fragment.xml片段。

Tomcat对jar中的web-fragment.xml的扫描主要通过JarScanner实现的,它也提供了配置来忽略哪些jar文件;在conf/catalina.properties里定义了tomcat.util.scan.DefaultJarScanner.jarsToSkip对哪些jar文件忽略。

使用spring-mvc框架的应用,依赖了一堆spring的jar,而这些jar没有在默认的忽略列表里,在扫描和合并web-fragment.xml的过程中会比较耗时。

Pandora的启动优化》上有5条评论

  1. viyond

    你好,请问现在外面有和Pandora解决问题类似的开源产品吗?

    回复
  2. viyond

    请问你现在所在的挖财,是使用tomcat + pandora这种结构吗?

    回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注