InfoQ上的一位架构师的演讲中的一话提醒了我,是否对于目前的测试案例,瓶颈并不是在线程池,而是在CPU等其他系统资源呢?
测试的这个业务模块中包括有数据库调用,业务数据的计算,日志,以及xml解析返回等等,可能会造成其他系统资源的瓶颈,既然目的是测试异步调用对容器线程充分利用的优势,那么应该突出线程资源这个主要矛盾。
修改需要调用的业务模块,使之只是简单的实现一个返回html的功能,而在其中使用线程休眠等待几百毫秒来模拟业务执行(保证只是时间上的等待而不占用更多的系统资源)。调整服务器配置,减少容器线程池中线程数,使容器线程资源紧张暴露出来。
服务器配置:
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="90" minSpareThreads="15" /> <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" processorCache="2000" acceptCount="100" URIEncoding="GBK" useBodyEncodingForURI="true"/>
Java代码:
传统方式:
package servlets; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CommonProcessServlet extends HttpServlet{ /** * 传统方式在同一个servlet线程内处理业务逻辑,处理完毕后返回,属于阻塞式。 */ private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"); out.println("<html>"); out.println("Hello,world!"); out.println("</html>"); } }
servlet 3.0异步方式:
package servlets; import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AsyncProcessServlet extends HttpServlet{ /** * 3.0异步调用在servlet线程内使用另一个异步线程来调用业务模块,容器线程直接返回,属于非阻塞式 */ private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ AsyncContext ctx = request.startAsync(); new Thread(new Executor(ctx)).start(); } } package servlets; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.AsyncContext; import javax.servlet.http.HttpServletResponse; public class Executor implements Runnable{ private AsyncContext ctx; public Executor(AsyncContext ctx){ this.ctx = ctx; } public void run() { try { Thread.sleep(200); } catch (InterruptedException e1) { e1.printStackTrace(); } HttpServletResponse res = (HttpServletResponse)this.ctx.getResponse(); res.setContentType("text/html"); PrintWriter out = null; try { out = res.getWriter(); } catch (IOException e) { e.printStackTrace(); } out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"); out.println("<html>"); out.println("Hello,World!"); out.println("</html>"); this.ctx.complete(); } }
压力测试结果(见附件):
传统方式,700并发:失败0%
传统方式,800并发:失败2.5%
传统方式,1000并发:失败16%
3.0异步方式,900并发:失败0%
3.0异步方式,1000并发:失败0%
3.0异步方式,1100并发:失败0%
后记,对于性能调试而言,要具体应用甚至具体交易具体分析。不能说一定是异步方式要优于同步处理方式,异步方式优势在于其非阻塞式的处理能够更有效的利用容器线程,提供吞吐能力,但是其在业务处理的过程中多启用了更多的线程,从而加大了线程切换时CPU的开销,对于CPU消耗型的应用或交易而言,异步方式便不是一个好的解决方案。所以对于性能问题,要找到当前影响性能的主要瓶颈,针对具体的问题采用不同的解决方案。