原文:http://www.onjava.com/pub/a/onjava/2003/04/02/multiple_submits.html
现代网络站点(web site)的主要任务是显示动态内容。从某些角度看,就是指用户将输入信息发送给网络应用(web application)进行处理之后网络应用再将处理结果发送回用户。某些特别情况下,从用户角度看后端操作运行足够快并且一切正常。但是在有些时候,后端的处理往往会因为出现较多的时间消耗而引起延迟。这种延迟有可能过长而最终使用户认为是其自己的操作错误,他们也许会放弃当前的操作或重新提交请求。
处理操作运行周期的事件过长并不是一个新问题。java提供的健壮的线程机制能够建立起后台的任务分配。另外,随着ejb 2.0规范的出现,基于消息的ejb(简称mdb)能够被用来执行后台操作。不过请记住,这些机制是为了处理异步操作而设计的。从你启动了一个线程或后台处理,到某段时间之后你被通知或者是需要查看结果,整个过程完全是异步的。
对于轻型的长时间运行的一般同步应用仍然会引起大量处理的问题你有何看法?想象一下,一个音乐爱好者(concert goer???)登陆她喜欢的网站为一场刚开始售票的演出订票(我想起了最近bruce springsteen的演唱会)。在通常情况下,网站会很好的执行并且我们的音乐爱好者能够以她自己的方式买到票。可是,当负载很大时,服务器便会慢下来,使该用户的操作不能顺利进行(用户会以为自己的订购操作失败了),因此她会接二连三的点击"提交"按钮。不幸的是,每一次点击"提交"都把之前的订票操作中止了。
有很多方法处理这种情况。最显而易见的方法是防止用户重复提交相同请求。另外也可以跟踪用户先前提交的请求并回复先前的提交动作。下图显示了一个简单的服务端小程序(servlet)的输出数据,该程序处理每一个收到的请求,为每一个请求分配一个票号。
处理简单的提交任务
图1:简单提交任务处理
最主要和最有效解决多重提交问题的方法就是防止这种情况发生。下面列出的html程序concerttickets.html是一个简单的表单,用于获取由用户输入的音乐会名字并提交给服务端小程序(servlet)订票。当网站相应迅速时处理执行的很好。但是,如果网站处于性能很低的状态并且提交的任务处理不够快,用户认为失败后会重新提交。处理过程在表1下的图2中显示。
表1:concerttickets.html
01: <html>
02: <head><title>online concert tickets</title></head>
03:
04: <center><h1>order tickets</h1></center>
05:
06: <form name="order" action="./simpleorder" method="get">
07: <table border="2" width="50%" align="center" bgcolor="cccccc">
08: <tr><td align="right" width="40%">concert: </td>
09: <td width="60%"><input type="text" name="concert" value=""></td></tr>
10:
11: <tr><td colspan="2" align="center">
12: <input type="submit" name="btnsubmit"
13: value="do submit"></td></tr>
14: </table>
15: </form>
16: </body>
17: </html>
图2:重复的任务提交
防止多重提交
最简单的解决多重提交问题的方法是防止这种情况发生。下面是我们表1中表单程序的一个修改,加入了少量的javascript脚本。内嵌的javascript脚本记录以前“提交”按钮被点击的次数。在用户再次提交时,将会弹出报警窗口并且表单不会再被提交。我们能够通过给“提交”按钮加入onclick事件属性来缩短普通提交处理过程的周期。每次提交按钮被点击时,onclick的代码就会执行。在我们这个例子中,会引起javascript脚本函数checksubmitcount()被调用。但仅仅调用一个函数并不会真正起到作用。如果我们只是加入onclick,每次提交按钮被点击时我们会收到弹出的警报,而任务提交也会立即发生。用户会被警告她操作错误,但是请求还是会被提交。这样做仅仅对用户端有了一定的改善。而在服务端结果是一样的:多重提交。
表2:concert2.html
01: <html>
. . .<!—与表1 :concerttickets.html程序02~11相同 //-->
12: <input type="button" name="btnsubmit"
13: value="do submit"
14: onclick="checksubmitcount();"></td></tr>
15: </table>
16: </form>
17:
18: <script language="javascript">
19: <!--
20: var submitcount = 0;
21: function checksubmitcount()
22: {
23: submitcount++;
24: if (1 == submitcount )
25: {
26: document.order.submit();
27: }
28: else
29: {
30: if ( 2 == submitcount)
31: alert("you have already submitted this form");
32: else
33: alert("you have submitted this form"
34: + submitcount.tostring()
35: + " times already");
36: }
37: }
38: //-->
39: </script>
40: </body>
41: </html>
我们能通过更进一步和更精细的改变我们网页的工作方式来解决问题。敏锐的读者可能会注意到对表单添加的改变。程序第12行定义的按钮类型原先为”submit”,现在改成了”button”。而网页界面是完全相同的。可是,与表单相关的默认动作(程序第6行,调用服务端小程序)不再是自动执行的了。我们现在能够通过程序控制表单向服务器端的提交,我们的问题也得到了解决,不是么?
matrix开源技术经onjava授权翻译并发布.
如果你对此文章有任何看法或建议,请到matrix论坛发表您的意见.
注明: 如果对matrix的翻译文章系列感兴趣,请点击oreilly和javaworld文章翻译计划查看详细情况
您也可以点击-fpwang查看翻译作者的详细信息
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器