Servlet、Jsp性能优化
Gagaghost译 (原文由Rahul Chaudhary所写)
你的J2EE应用是不是运行的很慢?它们能不能承受住上升的访问量?本文讲述了开发高性能和高弹性的JSP页面和Servlet的性能优化技术。其意思是建立尽可能快并能适应数量增长的用户和请求。在本文中,我将带领你学习已经实践和得到证实的性能调整技术,它将大大地推动你的servlet和jsp页面的性能,进而提升J2EE的性能。这些技术的部分用于开发阶段,例如,设计和编码阶段。另一部分技术则于配置相关。
技术1:使用HttpServlet init()方法缓存数据
服务器在创建Servlet实例之后和servlet处理任何请求之前调用servlet的init()方法。该方法在servlet的生命周期中仅仅调用一次。为了提高性能,Init()中可以用于缓存静态数据或者完成需要在初始化期间完成的昂贵的操作。例如,一个最好的实践是使用JDBC连接池,该连接池使用了javax.sql.DataSource接口。DataSource从JNDI树中获得。为了每一个SQL调用而执行JNDI查找DataSource是非常昂贵的并且严重影响了应用的性能。Servlet的init()方法应用用于获得DataSource并缓存它方便以后的重用:
public class ControllerServlet extends HttpServlet
{
private javax.sql.DataSource testDS = null;
public void init(ServletConfig config) throws ServletException
{
super.init(config);
Context ctx = null;
try
{
ctx = new InitialContext();
testDS = (javax.sql.DataSource)ctx.lookup("jdbc/testDS");
}
catch(NamingException ne)
{
ne.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public javax.sql.DataSource getTestDS()
{
return testDS;
}
...
...
}
技术2:禁用servlet和Jsp的自动装载功能
在你每次对Servlet/JSP做了修改之后,你将不得不重新启动服务器。由于自动装载功能减少开发时间,该功能被证实在开发阶段是非常有用的。然而,它在生产阶段是非常昂贵的;servlet/JSP由于不必要的装载和增加classloader的负担而造成很差的性能。同样,这会使你的应用在已经被某种classloader装载的类不能和当前classloader装载的类相互协作而出现很奇怪的冲突现象。因此,在生产环境中为了得到更好的性能,关闭servlet/JSP的自动装载功能。
技术3:控制HttpSession
许多应用程序需要一系列客户端的请求,因此他们能互相相关联。由于HTTP协议是无状态的,所以基于Web的应用负责维护这样一个叫做session的状态。为了支持必须维护状态的应用,Java servlet技术提供了API用于管理session和允许多中机制实现session。HttpSession对象扮演了session,但是使用它需要成本。无论何时HttpSession被使用和重写,它都由servlet读取。你可以通过使用下面的技术来提高性能:
技术4:使用gzip压缩
压缩是删除冗余信息的行为,用尽可能小的空间表达你所需要的信息。使用gzip(GNU zip)压缩文档能戏剧性的减少下载HTML文件的时间。你的信息量越小,它们被送出的速度越快。因此,如果你压缩了由你web应用产生的内容,它到达用户并显示在用户屏幕上的速度就更快。不是任何浏览器都支持gzip压缩,但是检查一个浏览器是否支持它并发送gzip压缩内容到浏览器是很容易的事情。下边的代码段说明了可能时如何发送压缩的内容。
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
OutputStream out = null
// Check the Accepting-Encoding header from the HTTP request.
// If the header includes gzip, choose GZIP.
// If the header includes compress, choose ZIP.
// Otherwise choose no compression.
String encoding = request.getHeader("Accept-Encoding");
if (encoding != null && encoding.indexOf("gzip") != -1)
{
response.setHeader("Content-Encoding" , "gzip");
out = new GZIPOutputStream(response.getOutputStream());
}
else if (encoding != null && encoding.indexOf("compress") != -1)
{
response.setHeader("Content-Encoding" , "compress");
out = new ZIPOutputStream(response.getOutputStream());
}
else
{
out = response.getOutputStream();
}
...
...
}
技术5:不要使用SingleThreadModel
SingleThreadModel保证servlet一次仅仅处理一个请求。如果一个servlet实现了这个接口,servlet引擎将为每个新请求创建一个单独的servlet实例,这将引起大量的系统开销。如果你需要解决线程安全问题,使用其他的方法替代这个接口。SingleThreadModel在Servlet 2.4中是不提倡使用。
技术6:使用线程池
servlet引擎为每个请求创建一个单独的线程,将该线程指派给service()方法,然后在service()方法执行完后删除该线程。默认情况下,servlet引擎可能为每个请求创建一个新的线程。由于创建和删除线程是很昂贵的,这种默认行为降低了性能。通过使用线程池可以提高性能。按照预期的并发用户数量,通过设置池子里的线程数和增长的最小和最大值配置一个线程池。起初,servlet引擎创建一个线程数与线程配置中的最小线程数量相等的线程池。然后servlet引擎把池中的一个线程指派给每个请求而不是每次都创建新的线程,之完成操作之后,该线程将被放回到线程池中。使用线程池,性能可以显著地提高。如果需要,根据线程的最大数和增长数,更多的线程可以被创建。
技术7:选择正确的包括机制
在JSP页面中,有两中方式可以包括文件:包括指令()和包括动作(<include page="test.jsp" flush="true">)。包括指令在翻译阶段包括一个指定文件的内容;例如,当一个页面转换成一个servlet时。包括动作指在请求阶段包括文件内容;例如,当一个用户请求一个页面时。包括指令要比包括动作更快些。因此除非被包括的文件经常变动,否则使用包括指令将会获得更好的性能。</include>
技术8:在useBean动作中使用合适的范围
一个最强有力使用JSP页面的方式是协同JavaBean组件使用。JavaBean使用<usebean>标签可以嵌入到JSP页面中。语法如下:</usebean>
<usebean class="br" id="name" scope="page|request|session|application"> "package.className" type="typeName"><br></usebean>
scope属性说明了bean的可见范围。Scope属性的默认值是page。你应该根据你应用的需求选择正确的范围,否则它将影响应用的性能。
例如,如果你为一个特别的请求中需要一个对象,但是你的范围设置成了session,那个对象在请求结束之后还保留在内存中。它将一直保留在内存中直到你明确地把它从内存中删除,使session无效或者如每个用servlet引擎配置的超时值一样session超时。如果你没有选择正确的范围属性,由于内存和垃圾收集的开销它将影响性能。因此为对象设置恰好的范围值并在使用完他们之后立即删除他们。
混合技术
总结
本文的目的是展示给你一些实践的和已经证实的很大推动servlet和JSP性能的性能优化技术,这将提高你的J2EE应用的性能。下一步应该观察其他相关技术的性能调整,如EJB、JMS和JDBC.