级别: 初级 Bill Zimmerly (bill@zimmerly.com), 自由撰稿人兼知识工程师, Author
2007 年 1 月 25 日 尽管计算机和 Web 使日常的任务变得更加便利,但它们也带来了一些新的挑战。如今的 Internet,其运行再也不仅仅限于对 HTML 编码的 Web 页面的请求做出响应。当今的 Web 站点必须能够维护大量的用户信息,并且能够管理许多复杂的任务。幸运的是,现在已经有一些前沿的工具能够用来简化这类状态问题。其中一种具有行业水准的解决方案脱颖而出,它就是会话状态。本文演示了 Apache Geronimo 如何能够维护成千上万个同时连接的状态,这样一来,IT 经理们就可以松口气了。
我们创建计算机和通信技术是用来简化生活的,不是吗?尽管表面看起来并非如此 —— 这些技术的确带来了其自身的一些复杂性 —— 但它们的确可以让我们实现一些原来所不能实现的事情,从这个意义上讲,它们确实让人们的生活变得简单和方便了。
例如,现在您可以从早餐桌走到起居室的电脑旁来查看一下当前的气象雷达或道路状况,或者订购一盆新的室内植物,当然也可以结算支票簿、支付账单等等,而这在几十年前(花如此少的力气)简直是不可想象的。这确实是一种进步,但其产生也少不了路途中的一些磕磕绊绊。
Web 过去的好时光
在过去,从某种意义上讲,万维网(World Wide Web)就是一个电子的超级高速公路,在这里,那些敢于开拓的公司用特异的布告牌,以当时能够实现的最佳视觉方式展示其产品。人们被其吸引进而会阅读这些布告牌,然后打电话给这些公司了解更多关于产品的信息,再通过传统的方式来完成交易:电话交易或亲自前往(购买)。
没过多久,计算机程序员们就为发生在布告牌(其实是 Web 站点本身)上的买卖双方的交易创建了一种更为交互的方式。从而,联系我们 窗体、粗糙的购物车及其他简单的 Web 应用程序就诞生了。
从前,Web 站点只需要对超文本标记语言(HTML)编码的 Web 页面的请求做出响应。现在,Web 站点必须记住访客的信息,这些信息是同该访客进行交易所必需的。
突然间,人们不得不在登录 Web 站点的时候提供这样的信息,即是谁、想买什么、想以什么方式购买,Web 站点必须维护这些信息。Web 服务器再也不能这样简单地答复其调用者了,即向他/她发送一个页面后就完事大吉。按照计算机界的说法,这些任务即状态问题 ,这个问题将 Web 在其幼年时期曾经享用的那种简单的请求-响应的模式复杂化了。
如今,我们都处在这样一个时代,每个想要让其商业及其业务模式在 21 世纪能够生存下来的公司都有一个 Web 站点,状态问题(由此变得)至关重要。如果用户 12317 不能在线购买 1437 公司的 8945 号产品,该用户就能够在从起居室的电脑旁走到厨房找些吃的东西之前,利用搜索引擎找到能够将该商品卖给他/她的 4783 公司,换言之,一切都太方便了。
但为购买产品的用户简化了购买过程即意味着要有其他人为这部分无形的价格买账,世上没有免费的午餐,这是一个不言而喻的道理。Web 服务器和 Web 应用程序是包含了许多独立组件的复杂系统,这些组件本身又有其复杂性:数据库引擎、安全机制、信用卡验证引擎等等。
减轻职业 IT 人的负担
免费(软件)市场像往常一样出来救援了。如今的 Internet 工程师们能够从大量工具和供应商产品中挑选,这些工具和产品让商业的车轮仍旧能够向前滚动并一路高歌。要获取针对状态问题的行业水准的解决方案,请考虑由 Apache Geronimo 为代表的 Java™ 2 Platform, Enterprise Edition(J2EE™)引擎。
在典型的大型业务程序中,每个小时中,都会调用单独的应用程序来对成千上万的匿名浏览器发出的无休止的请求系列做出响应。当然,几百个左右的浏览器自身会带有一些服务器软件所熟悉的信息,这些浏览器就是那些带 cookie 的客户机浏览器。
这些 cookie 最初就被植入到匿名的用户 Web 浏览器上,从而可以确认用户这次是否是再次光顾 —— 辨认出老客户对公司来说是件好事!客户机状态集就是在这里存储的。Cookie 也许仅仅由一个简单的键值组成,该值用于解锁存储于公司数据库中有关此用户的大量信息。
当一个已知客户再次光顾且(服务器)认出了该用户的浏览器所提供的 cookie 时,状态问题就解决了一半。很快,服务器软件能够用各种有用信息定制页面,如个人化的欢迎页面或首选的偏好页面。还会显示一份与用户过去购买的产品相似的产品清单和一些诱人的折扣,这些折扣基本上向用户传达了这样的信息,即他或她是一名宝贵的客户,因而公司非常欢迎该用户的再次光顾,希望其购买更多的东西。
客户机状态
用 Internet 工程师们的行话讲,这种发生在一个经过认证的客户机浏览器和服务器间的 Web 站点的对话叫做会话 。会话的状态 (已保存的关于客户及其购买习惯的信息)保存在服务器的超文本传输协议(HTTP)的范围之外,因为根据定义,它是一个无状态的协议。.
 |
Geronimo 会话样例
可以通过查看由 Geronimo 安装程序提供的 servlet 样例来查看这个会话的结果。为访问这个样例,请打开 Geronimo 安装程序的默认主页。然后单击在 Geronimo Examples 标题下面的 Servlet Examples 。单击该 Session 样例的 Execute 链接。每次键入会话属性的名称和值对时,该会话会保留它们并为所有随后对该 Web 站点的访问回调它们(只要不清理浏览器的 cookie)。
|
|
Geronimo 中的 Java servlet 技术提供了自动创建并管理会话的应用编程接口(API)。这项功能的核心驻留在 HttpSession 对象中,可以通过调用请求对象的 getSession() 方法来访问会话。此方法返回与此请求相关联的当前会话;如果没有这样的会话,该方法会自动创建一个并返回新创建的会话句柄。欲了解更多信息,请参见侧栏中的 Geronimo 会话样例 。
如果将 cookie 用作会话跟踪机制(还有其他方法),servlet 代码中的 getSession() 方法能够修改响应标头信息 —— 特别是通过自动创建一个新会话(如果该请求尚不具备与之相关联的会话时)。诸如这样的功能是令人叫绝的。会话以及它们可以从持久存储中创建并回调对于那些已经十分繁忙的程序员们来说真是一个 “救命” 的机制。
为简化并自动化传递代表这个 用户状态的其他信息,可以用名称=值的方式将属性值和会话关联起来。如 清单 1 中的代码片段所示。
清单 1. ShoppingCartServlet
public class ShoppingCartServlet extends HttpServlet {
public void doGet (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Get session and current shopping cart information.
HttpSession theSession = request.getSession();
TheShoppingCart theCart = (TheShoppingCart) session.getAttribute(thecart);
.
.
.
// Calculate the total price of all items in the cart.
double totalPrice = theCart.getTotal();
|
大量的处理都在其幕后进行,这些处理居于 Geronimo 的核心来维护之前所描述过的状态问题,并且 Java servlet 机制会掩藏这些细节。信息存储于请求之间,所以总能知道一个给定用户和公司的(交易)历史记录。
实现维护客户机浏览器状态的 HttpSession 方法的应用程序也具有和其使用相关联的重要的附加功能。在这些附加的功能中,其中一种功能是能够用来自动通知会话关系中其他对象的。例如,如果一些对象实现了 javax.http.HttpSessionBindingListener 接口,那么它们就可以被告知它们已经被添加到一个会话中或已经从一个会话中删除。
如果与对象相关联的会话是被动的或主动的,如在虚拟机(VM)间或持久性存储间移动,当该对象实现了 javax.http.HttpSessionActivationListener 接口时,就能够向该对象通知这项改变。
像这样的事件通知让对象能够在特定的时间完成与正在发生事件密切相关的事情,如将会话已经发生这一事实记入日志,这将导致会记录下更多的用户购物习惯。可以用这种方式触发各种各样的高级数据挖掘规则。
服务器状态
服务器上进行的会话管理包括许多任务,这些任务也许在直观上并不明显。但每个会话都消耗服务器资源(特别是内存),这些资源必须在会话超时时收回。会话的超时值依赖于许多因素,对这些因素的详细介绍超出了本文讨论的范围,可以用应用程序的部署工具来设定该值。或者,会话对象本身可以通过调用 getMaxInactiveInterval() 或 setMaxInactiveInterval() 方法来获取或设定该超时值。
每个会话都有一个与其相关联的生存时间计数器,一个比较好的做法是定时地使用服务方法来访问该会话,因为这样做会重置计数器且会阻止会话的超时(实例化及终止会话会消耗大量的 CPU 和内存资源,因而最好是在合理的时间内保持会话的活动)。
这就是为什么要向用户显示 Sign out 或 Log off 这类的按钮并鼓励用户去使用这些按钮的原因之一。当用户单击该按钮时,实质上告知服务器该会话已经结束,随后会调用该会话的 invalidate() 方法来清理服务器上该会话的数据并收回前面提到的资源。清单 2 中的代码说明了这一过程。
清单 2. FinishServlet
public class FinishServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Get session and shopping cart information and end the session.
HttpSession theSession = request.getSession();
theSession.invalidate();
|
跟踪难以捉摸的客户
如前所述,有几种可用的方法将会话信息和特定的用户关联起来,所有这些方法都要求在客户机和服务器计算机间传递一定种类的标识符。已经提到的一种方法是 cookie 方法。另一种方法是将该标识符包含到每一个后来发送回客户机浏览器的 URL 中。这个标识符(通常是数据库键)能够作为该 URL 参数之一而生成并发送到客户机。很肯定地说,如果您看到和下列相似的 URL 时,就说明您正在被跟踪:http://www.somedomain.com/applications/thecart?jsessionid=868D6879824E0F3FB499D8146A9EE7F
这个叫做 jsessionid 的十六进制数字是到 Geronimo 会话句柄的主键,并为系统提供了关于您的信息(您在过去某时提供的)。这是一个会话编号(准备好再现关于您的数据),它存储在之前对 Web 站点访问的持久性存储中。使用该方法维护状态有诸多优势,最显著的是它并不依赖用户浏览器 cookie 机制的打开(正如时下许多人所做的)。
Web 应用程序必须重写每一个在 Web 页面中发送给用户的 URL,否则将丢失该会话信息。如果用 J2EE 的方式(因而也是 Geronimo 方式)来实现,就要对所有由 servlet 返回的 URL 调用响应对象的 encodeURL() 方法。这样,只有当用户浏览器关闭了 cookie 处理,生成的 URL 中才会包含会话 ID。否则,浏览器返回未改变的 URL,例如:
out.println("<a href=\"" +
response.encodeURL(request.getContextPath() +
"/thecart") + "\">Continue Shopping?</a>");
如果用户关闭了浏览器中的 cookie,将看到与此类似的(显示):http://www.somedomain.com/applications/thecart?jsessionid=868D6879824E0F3FB499D8146A9EE7F 。
但如果 cookie 机制仍打开着,将看到与此类似的(显示):http://www.somedomain.com/applications/thecart 。
结束语
通过使用顶级的工具,如 Geronimo 及其所实现的 J2EE 技术,现代 Web 应用程序所必须维护的许多状态问题都可以得到极大的简化。如果您是一个珍惜自己睡眠时间的公司经理,那么您尽可以高枕无忧,因为 Geronimo 架构可以为您的客户提供愉快的体验以使其在下次光顾时仍能保持愉悦;该架构还可以让您的程序员们也非常满意,因为它可以为程序员们提供一个可靠的环境以使维护用户状态变得十分简单。
参考资料 学习
获得产品和技术
讨论
关于作者  | 
|  | Bill Zimmerly 是一名知识工程师和专长于不同版本 UNIX® 和 Microsoft® Windows® 软件的底层系统程序员,同时他也是一名崇尚逻辑至上的自由思想者。他热衷于创建新技术并撰写相关的文章。他住在 Missouri 的 Hillsboro 郊外,那里空气新鲜、景色怡人,到处都有上等的葡萄酒厂。 |
对本文的评价
|