快捷搜索:

Java多重提交处理分析

今世收集站点(web site)的主要义务是显示动态内容。从某些角度看,便是指用户将输入信息发送给收集利用(web application)进行处置惩罚之后收集利用再将处置惩罚结果发送回用户。某些分外环境下,从用户角度看后端操作运行足够快并且统统正常。然则在有些时刻,后真个处置惩罚每每会由于呈现较多的光阴耗损而引起延迟。这种延迟有可能过长而终极应用户觉得是其自己的操作差错,他们大概会放弃当前的操作或从新提交哀求。

处置惩罚操作运行周期的事故过长并不是一个新问题。Java供给的壮实的线程机制能够建立起后台的义务分配。别的,跟着EJB 2.0规范的呈现,基于消息的EJB(简称MDB)能够被用来履行后台操作。不过请记着,这些机制是为了处置惩罚异步操作而设计的。从你启动了一个线程或后台处置惩罚,到某段光阴之后你被看护或者是必要查当作果,全部历程完全是异步的。

对付轻型的长光阴运行的一样平常同步利用仍旧会引起大年夜量处置惩罚的问题你有何见地?想象一下,一个音乐喜欢者登岸她爱好的网站为一场刚开始售票的表演订票(我想起了近来Bruce Springsteen的演唱会)。在平日环境下,网站会很好的履行并且我们的音乐喜欢者能够以她自己的要领买到票。可是,当负载很大年夜时,办事器便会慢下来,使该用户的操作不能顺利进行(用户会以为自己的订购操作掉败了),是以她会持续不断的点击"提交"按钮。不幸的是,每一次点击"提交"都把之前的订票操作中止了。

有很多措施处置惩罚这种环境。最显而易见的措施是防止用户重复提交相同哀求。别的也可以跟踪用户先条件交的哀求并回覆先前的提交动作。下图显示了一个简单的办事端小法度榜样(servlet)的输出数据,该法度榜样处置惩罚每一个收到的哀求,为每一个哀求分配一个票号。

处置惩罚简单的提交义务

图1:简单提交义务处置惩罚

screen.width-333)this.width=screen.width-333;">

最主要和最有效办理多重提交问题的措施便是防止这种环境发生。下面列出的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:重复的义务提交

screen.width-333)this.width=screen.width-333;">

防止多重提交

最简单的办理多重提交问题的措施是防止这种环境发生。下面是我们表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行,调用办事端小法度榜样)不再是自动履行的了。我们现在能够经由过程法度榜样节制表单向办事器真个提交,我们的问题也获得了办理,不是么?

表2当然是一种改良,但我们照样必要一些其他措施。仍旧有许多环境会导 致差错。假如用户按下了浏览器的退却撤退键或者刷新了全部网页会怎么样?假如用户的浏览器关闭了javascript的功能或者浏览器不能处置惩罚会若何呢?我们照样可以处置惩罚这个问题的,然则为了取代防止多重提交,我们必要在后端经由过程表单处置惩罚的办事端小法度榜样处置惩罚它们。

为了理解若何办理多重提交问题,我们必须首先理解办事端小法度榜样会话(sessions)机制。每小我都知道,HTTP协议的固有性子中并纰谬状态(客户端哀求信息的历史记录)进行记录。为了处置惩罚状态,我们必要一些措施使浏览器能够将当前哀求与其他大年夜量哀求联系起来。会话(session)法度榜样供给给我们一个办理这个问题的规划。HttpServlet中的措施doGet()和doPost()应用了两个指定的参数:HttpServletRequest和HttpServletResponse。办事端小法度榜样哀求参数使我们能够造访会话(session)。会话为造访和存储状态供给了机制。

什么才是会话(session)呢?会话(session)包括很多内容:

状态集??由web办事器治理并且由特定用户所有哀求所共享的具体表示描述。

存储空间??经由过程HttpSession接口至少将HttpServlets所需状态数据和定义存储起来。

在我们详细懂得若何应用办事器端规划办理我们的问题之前,我们还必要懂得办事端小法度榜样会话(session)的生命周期。与EJB及其他办事端实体一样,会话在生计期中经由过程一个定义的状态集运行。下图显示了会话的生命周期。Servlet可在三种特定的状态中转换:不存在(does not exist),新建(new),非新建(not new/或应用中in-use)。

图3:办事端小法度榜样会话(session)生命周期

[ 相关贴图 ]

a) 会话在开始时处于不存在状态。会话从这一状态开始或者因为许多缘故原由而返回到此状态。最主要的缘故原由便是用户曩昔没有造访过这些状态或者是因为用户离开(超时)站点或退出使会话被设置为无效。

b) 当会话被树马上便会从“不存在”状态进入“新建”状态。新建与非新建状态的区分是异常紧张的,由于HTTP协议不记录状态信息。根据servlet具体阐明书描述,在客户端返回会话给办事端之前会话不能够进入非新建状态(即从预期会话转变为当前会话)。这样在客户端不知道或者还没有抉择加入会话时会话处于新建状态。

c) 当会话经由过程cookie或是重写URL()返回到办事器时,会话就变为“应用中”或“非新建”状态。

d) 经由过程各类get与set措施继承应用会话会使其保持在“应用中”状态。

e) 当会话因为长光阴没有被应用而超时或显式的被设为无效则会发生图中所示的5以及6所标识的转移。不合利用办事器用不合要领处置惩罚超时。BEA公司的WebLogic使利用支配者能够经由过程与web利用一路打包的特殊支配描述脚本(weblogic.xml)设置会话超时的时限。

现在我们懂得了会话的生命周期,那么若何得到一个会话并有效的应用它呢?接口HttpServletRequest供给了两个关于会话的措施:

public HttpSession getSession()返回一个新的会话或一个已存在的会话。

假如供给一个有效的会话ID(可能是经由过程cookie)则返回一个存在的会话。返回新的会话可能会有许多缘故原由:用户最初的会话(无法供给有效会话ID);会话跨越有效光阴(供给了会话ID);一个无效的会话(供给了会话ID);或者是明确指出会话无效(供给了会话ID)。

public HttpSession getSession(boolean)可能返回新会话、存在的会话或者空。getSession(true)尽可能返回一个存在的会话。否则创建一个新会话。getSession(false) 尽可能返回一个存在的会话否则返回空。

我们照样只办理了手边一半的问题。我们盼望能够跳过会话“新建”状态并自动的转换到会话“应用中”状态。我们能够经由过程重定向浏览器到处置惩罚办事端小法度榜样自动的实现这些。表3把办事端小法度榜样会话逻辑和重定向用户端与有效会话到处置惩罚办事端小法度榜样的能力结合在一路。

表3:RedirectServlet.java

01: package multiplesubmits;

02:

03: import java.io.*;

04: import java.util.Date;

05: import javax.servlet.*;

06: import javax.servlet.http.*;

07:

08: public class RedirectServlet extends HttpServlet{

09: public void doGet (HttpServletRequest req, HttpServletResponse res)

10: throws ServletException, IOException {

11: HttpSession session = req.getSession(false);

12: System.out.println("");

13: System.out.println("-------------------------------------");

14: System.out.println("SessionServlet::doGet");

15: System.out.println("Session requested ID in Request:" +

16: req.getRequestedSessionId());

17: if ( null == req.getRequestedSessionId() ) {

18: System.out.println("No session ID, first call,

creating new session and forwarding");

19: session = req.getSession(true);

20: System.out.println("Generated session ID in Request: " +

21: session.getId());

22: String encodedURL = res.encodeURL("/RedirectServlet");

23: System.out.println("res.encodeURL("/RedirectServlet");="

+encodedURL);

24: res.sendRedirect(encodedURL);

25: //

26: // RequestDispatcher rd = getServletContext().getRequestDispatcher(encodedURL);

27: // rd.forward(req,res);

28: //

29: return;

30: }

31: else {

32: System.out.println("Session id = " +

req.getRequestedSessionId() );

33: System.out.println("No redirect required");

34: }

35:

36: HandleRequest(req,res);

37: System.out.println("SessionServlet::doGet returning");

38: System.out.println("------------------------------------");

39: return;

40: }

41:

42: void HandleRequest(HttpServletRequest req, HttpServletResponse res)

43: throws IOException {

44: System.out.println("SessionServlet::HandleRequest called");

45: res.setContentType("text/html");

46: PrintWriter out = res.getWriter();

47: Date date = new Date();

48: out.println("<html>");

49: out.println("<head><title>Ticket Confirmation</title></head>");

50: out.println("<body>");

51: out.println("<h1>The Current Date And Time Is:</h1><br>");

52: out.println("<h3>" + date.toString() + "</h3>");

53: out.println("</body>");

54: out.println("</html>");

55: System.out.println("SessionServlet::HandleRequest returning");

56: return;

57: }

58: }

这若何办理我们的问题的呢?测试上面这段代码显示出在11行我们考试测验得到一个会话的句柄。在17行我们经由过程检测为空的会话ID或检测有效会话ID来确定存在一个有效的会话。假如不存在会话就履行18-29行法度榜样。经由过程下述措施我们处置惩罚多重提交的问题,在19行首先建立一个会话,在22行应用URL编码添加新会话ID,并且在24行重定向我们的办事端小法度榜样到新的URL编码。

不认识重写URL的读者可参考15行到23行。一个HttpServlet工具可以重写URL。这个历程将一个会话ID插入到URL。底层的利用办事器能够自动的用编码URL供给给办事端小法度榜样或JSP一个存在的会话。因为这依附于利用办事器,为了使上面的例子可以运行,你可能必要设置情况使URL能够重写!

总结

在这篇文章中,我们评论争论了多重提交问题的许多办理规划。每一个规划都有优点和缺陷。在处置惩罚问题时,要清晰的理解和权衡办理规划多方面的优点和缺陷。我们着末的例子有利于办理客户端额外重复的造访和浏览的问题。javascript脚本的措施是最好的,然则必要客户端支持才能够运行。和其他任何问题一样,会有一大年夜堆办理规划,每个规划都邑有其自己的优毛病。掌握每个规划的优毛病,有利于我们为办理问题作出最好的选择。

摘自:天极网   光阴:2003年10月1日

您可能还会对下面的文章感兴趣: