为了提高性能和减少用户等待时间,我们常用response.flushBuffer方法来分批输出已经转换好的页面。这样内容可以分块逐步显示在网页上,用户不用长时间面对一个空白的页面等待。但在集群系统中,当某个服务器瘫痪从而引起服务迁移情况下,问题就来了,用户可能会看到重复的内容。
这里我们做个实验,写如下一段代码:
response.setContentType("text/html");
response.setContentLength(500);
//不同的机器用不同的颜色和名字
String color=this.getInitParameter("Color");
String server=this.getInitParameter("Server");
PrintWriter writer = response.getWriter();
writer.println("<html><body>");
writer.println("<h1><font color=\"" + color + "\">Session serviced by tomcat " + server + "</font></h1>");
writer.println("<table aligh=\"center\" border=\"1\">");
writer.println("<tr><td>Session ID</td><td>");
writer.println(request.getSession(true).getId());
writer.println("</td>");
writer.println("</tr><tr><td>Created on</td><td>");
writer.println(request.getSession().getCreationTime());
writer.println("</td></tr></table>");
writer.println("<form method=\"POST\" action=\"LongTask\">");
writer.println("<Input name=\"test\" value=\"\">");
writer.println("<INPUT TYPE=submit name=\"submit\" value=\"update\">");
writer.println("</form>");
//提前把前面的内容输出
writer.flush();
Object ob = request.getParameter("test");
if (ob!=null) {
request.getSession().setAttribute("test",ob);
try {
String time=this.getInitParameter("WaitTime");
//这里模拟一个长时间的任务,以便有机会手动把一个机器停掉
Thread.sleep(Integer.parseInt(time));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
writer.println(request.getSession().getAttribute("test"));
writer.flush();
writer.close();
writer.println("</body></html>");
我们用Apache 2.2 + mod_jk + Tomcat 6.0 (In-memory session replication)来架设集群。注意要在httpd.conf的配置中设置Flush选项:
JkWorkersFile conf/workers.properties
JkOptions +FlushPackets
JkMount /examples/jsp/* bal1
JkMount /jkstatus/ stat1
mod_jk的配置
worker.list = bal1,stat1
worker.Tomcat6A.type = ajp13
worker.Tomcat6A.host = localhost
worker.Tomcat6A.port = 8009
worker.Tomcat6A.lbfactor = 10
worker.Tomcat6B.type = ajp13
worker.Tomcat6B.host = localhost
worker.Tomcat6B.port = 8010
worker.Tomcat6B.lbfactor = 10
worker.Tomcat6C.type = ajp13
worker.Tomcat6C.host = localhost
worker.Tomcat6C.port = 8011
worker.Tomcat6C.lbfactor = 10
worker.bal1.type = lb
worker.bal1.sticky_session = 1
worker.bal1.balance_workers = Tomcat6A, Tomcat6B, Tomcat6C
worker.stat1.type = status
我们把上段代码编译成名字为LongTask的servlet,并放到Tomcat自带的例子examples下面。架设三个Tomcat实例在三台机器上。(本文为了方便都放到一台机器localhost上)。当一台机器正在处理请求时,我们手动停掉它。测试结果如下:
图1:发生故障前
图2:发生故障后
除了数据重复外,Session数据也丢失了(原因还未知). 但如果关闭分批输出,则没有这些问题.