我们正在使用Apache Tomcat 6.0和Jetty 6。我们主要使用Jetty进行测试(它非常适合在JUnit测试中运行嵌入式)和Tomcat用于生产。
默认情况下,Tomcat会在用户请求时动态编译JSP。但这会导致第一次打击的性能下降。它还突出显示了Tomcat JSP编译器中的bizarre bugs。
Tomcat documentation提供了在构建时使用Ant预编译JSP的建议(并且还提供了Maven插件)......但是生成的WAR包含特定于Tomcat的内容,例如: PageContextImpl.proprietaryEvaluate,所以我们不能在Jetty中使用它。
是否有一些标志或设置我们可以在某处使用强制Tomcat在WAR初始化时预编译所有JSP?我们准备在启动时等待一段时间。
事先:我知道有一种方法可以通过在web.xml中为一个JSP显式标识/ servlet / load-on-startup标记来预编译一个 JSP。但对于几十甚至几百个无法管理的JSP。
答案 0 :(得分:4)
http://www.devshed.com/c/a/BrainDump/Tomcat-Capacity-Planning/
project name="pre-compile-jsps" default="compile-jsp-servlets">
<!-- Private properties. -- >
<property name="webapp.dir" value="${basedir}/webapp-dir"/>
<property name="tomcat.home" value="/opt/tomcat"/>
<property name="jspc.pkg.prefix" value="com.mycompany"/>
<property name="jspc.dir.prefix" value="com/mycompany"/>
<!-- Compilation properties. -->
<property name="debug" value="on"/>
<property name="debuglevel" value="lines,vars,source"/>
<property name="deprecation" value="on"/>
<property name="encoding" value="ISO-8859-1"/>
<property name="optimize" value="off"/>
<property name="build.compiler" value="modern"/>
<property name="source.version" value="1.5"/>
<!-- Initialize Paths. -->
<path id="jspc.classpath">
<fileset dir="${tomcat.home}/bin">
<include name="*.jar"/>
</fileset>
<fileset dir="${tomcat.home}/server/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${tomcat.home}/common/i18n">
<include name="*.jar"/>
</fileset>
<fileset dir="${tomcat.home}/common/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${webapp.dir}/WEB-INF">
<include name="lib/*.jar"/>
</fileset>
<pathelement location="${webapp.dir}/WEB-INF/classes"/>
<pathelement location="${ant.home}/lib/ant.jar"/>
<pathelement location="${java.home}/../lib/tools.jar"/>
</path>
<property name="jspc.classpath" refid="jspc.classpath"/>
<!--========================================================== -->
<!-- Generates Java source and a web.xml file from JSP files. -->
<!-- ==========================================================
-->
<target name="generate-jsp-java-src">
<mkdir dir="${webapp.dir}/WEB-INF/jspc-src/${jspc.dir.prefix}"/>
<taskdef classname="org.apache.jasper.JspC" name="jasper2">
<classpath>
<path refid="jspc.classpath"/>
</classpath>
</taskdef>
<touch file="${webapp.dir}/WEB-INF/jspc-web.xml"/>
<jasper2 uriroot="${webapp.dir}"
package="${jspc.pkg.prefix}"
webXmlFragment="${webapp.dir}/WEB-INF/jspc-web.xml"
outputDir="${webapp.dir}/WEB-INF/jspc-src/${jspc.dir.prefix}"
verbose="1"/>
</target>
<!-- ========================================================== -->
<!-- Compiles (generates Java class files from) the JSP servlet -->
<!-- source code that was generated by the JspC task. -->
<!-- ========================================================== -->
<target name="compile-jsp-servlets" depends="generate-jsp-java-src">
<mkdir dir="${webapp.dir}/WEB-INF/classes"/>
<javac srcdir="${webapp.dir}/WEB-INF/jspc-src"
destdir="${webapp.dir}/WEB-INF/classes"
includes="**/*.java"
debug="${debug}"
debuglevel="${debuglevel}"
deprecation="${deprecation}"
encoding="${encoding}"
optimize="${optimize}"
source="${source.version}">
<classpath>
<path refid="jspc.classpath"/>
</classpath>
</javac>
</target>
<!-- ========================================================= -->
<!-- Cleans any pre-compiled JSP source, classes, jspc-web.xml -->
<!-- ========================================================= -->
<target name="clean">
<delete dir="${webapp.dir}/WEB-INF/jspc-src"/>
<delete dir="${webapp.dir}/WEB-INF/classes/${jspc.dir.prefix}"/>
<delete file="${webapp.dir}/WEB-INF/jspc-web.xml"/>
</target>
</project
此构建文件将查找所有webapp的JSP文件,将它们编译为servlet类,并为这些JSP servlet类生成servlet映射。它产生的servlet map ping必须进入你的webapp的WEB-INF / web.xml文件,但是编写一个知道如何以可重复的方式将servlet映射插入web.xml文件的Ant构建文件是很困难的。构建文件运行的时间。相反,我们使用XML实体包含,以便每次构建文件运行时生成的servlet映射都会进入新文件,并且可以通过XML实体包含机制将servlet映射文件插入到web.xml文件中。要使用它,您的webapp的WEB-INF / web.xml必须在文件顶部有一个特殊的实体声明,另外还有一个对web.xml文件内容中实体的引用,您希望servlet映射文件在该文件中包括在内。以下是一个空的servlet 2.5 webapp的web.xml文件如何看待这些修改:
<!DOCTYPE jspc-webxml [
<!ENTITY jspc-webxml SYSTEM "jspc-web.xml">
]>
<web-app xmlns=http://java.sun.com/xml/ns/javaee
xmlns:xsi=http://www.w3.org/2001/ XMLSchema-instance
xsi:schemaLocation="http:// java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/
javaee/web-app_2_5.xsd"
version="2.5">
<!-- We include the JspC-generated mappings here. -->
&jspc-webxml;
<!-- Non-generated web.xml content goes here. -->
</web-app>
确保您的webapp的web.xml文件在文件顶部和下面的servlet 2.5 web-app架构声明中一直有内联DTD(DOCTYPE标记)。然后,无论您希望在web.xml文件中插入生成的servlet映射,请将实体引用&amp; jspc-webxml; 。请记住,实体引用以&符号(&amp;)开头,然后是实体的名称,以分号(;)结尾。
要使用构建文件,只需编辑它并将顶部的所有属性设置为与您的设置匹配的值,然后像这样运行:
$ ant -f pre-compile-jsps.xml
答案 1 :(得分:1)
如果你选择duffymo提到的解决方案指向Vinny Carpenter的博客,我有一个提示。有一个区域导致我的容器在联系localhost时无限期挂起(特别是在private connect()方法中)。使用以下hack是我的解决方法:
private void connect(final String urlString) {
HttpURLConnection conn;
try {
final URL url = new URL(urlString);
conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5000);
//time it out quickly - otherwise hangs forever
//seems to be an issue hitting localhost
//will still precompile the page
conn.setReadTimeout(100);
conn.setAllowUserInteraction(true);
conn.getInputStream();
conn.disconnect();
}
catch (SocketTimeoutException e) {
log.debug(e);
}
catch (IOException ioe) {
log.error(ioe);
}
}
设置超时并忽略SocketTimeoutException(虽然不可能是最佳解决方案)。此外,使用此过程意味着您需要在web.xml中指定JSP。这足以满足我的需求。