HTTP协议是一种无状态的协议,而对于现在的web应用而言,我们往往需要记录从特定客户端的一系列请求间的联系。
现在已经有很多会话跟踪的技术,但是对于程序员而言都不是很方便直接使用。servlet规范定义了一个简单的
HttpSession接口以方便servlet容器进行会话跟踪而不需要开发者注重实现的细节。
一般而言,有两种最常用的会话跟踪机制,一种就是URL重写。在客户端不接受cookie的情况下可以使用URL重写进行会话跟踪。
URL重写包括向URL路径添加一些容器可以解释的数据。规范要求会话ID必须编码在URL路径中,参数名称必须是jsessionid,
例如: http://www.myserver.com/catalog/index.Html;jsessionid=1234
另一种就是现在最常用的cookie了,规范要求所有的servlet都必须支持cookie。容器向客户端发送一个cookie,客户端在后续的处于同一个会话的请求中向服务器返回该cookie。会话跟踪cookie的名字必须是JSESSIONID。
新出现的一种会话功能是SSL会话,SSL(Secure Sockets Layer,安全套接字层)是HTTPS协议使用的一种加密技术,内建了会话跟踪功能,servlet容器可以非常轻易的使用这些数据建立会话跟踪。(但是HTTPS不是规范要求servlet必须支持的协议) 因为HTTP是一种基于请求响应的协议,因此会话只有在客户端加入它以后才被新建立。当会话跟踪信息被成功的返回给服务器以指示会话给建立时客户端才算加入了一个会话。假如客户端没有加入会话,那么下一次请求不会被认为是会话的一部分。如何客户端还不知道会话或者客户端选择不加入一个会话,那么会话被认为是新的。开发者必须自己设计自己的应用中的会话处理状态,在什么地方没有加入会话,什么地方不能加入会话以及什么地方不需要加入会话。
规范要求HttpSession在应用或者servlet上下文级别有效,诸如cookie这样的建立会话的底层机制可以在上下文中共享,但是对于那些外露的对象,以及更重要的是对象的那些属性是不能在上下文中共享的。
对于会话的属性的绑定而言,任何对象都可以绑定到某个命名属性。被绑定的属性对象对于其它处于相同ServletContext并且处于同一个会话处理中的其它servlet也是可见的。
某些对象在被加入会话或者被从会话中移除时要求得到通知,这样的信息可以通过让该对象实现HttpSessionBindingListener接口得到。
该接口定义了两个方法用以标记被绑定到会话或者从会话中被移除。 valueBound方法在对象通过getAttribute之前就被调用,而valueUnbound方法在对象已经不能通过getAttribute得到后才被调用。
由于HTTP是无状态协议,因此客户端不再活动时没有什么明显的信号,这也就意味着只有一种机制可以用于表明客户端不再活动:超时。
会话的缺省的时限由servlet容器定义并且可以通过HttpSession的getMaxInactiveInterval得到,开发者也可以通过使用setMaxInactiveInterval
方法进行设置,这些方法返回的单位是秒,假如时限被设置为-1,那么意味着永远不会超时。
通过调用HttpSession的getLastAccessedTime方法,我们可以得到在当前请求之前的访问时间。当会话中的一个请求被servlet上下文处理时会话就被认为被访问了。
另外需要注重的就是一些很重要的会话的语义问题。
多线程问题:多个请求线程可能会同时访问同一个会话,开发者有责任以适当的方式同步访问会话中的资源。
分布式环境:对于被标记为可分布的应用而言,同一会话中的所有请求只能被单一的VM处理。同时,放入HttpSession中的所有对象都必须实现Serializable接口,否则容器可能会抛出IllegalArgumentException(在jboss_tomcat下没有抛出这个异常,但是假如在关闭服务器时还有未完成的会话,那么服务器在试图存储会话时会出现串行化异常,在重新启动的时候会试图回复会话,也会出现异常)。
这个限制意味着开发者不会碰到非可分布容器中的那些并发问题。另外容器提供者可以通过将一个会话对象以及它的内容从分布式系统的一个活动节点移动到系统的其它不同节点的能力来保证可伸缩性。
客户端的语义:基于cookie或者SSL证书通常是被web浏览器控制并且不联系到特定浏览器窗口的事实,从客户端应用的所有窗口发送到容器的请求都可能是同一个会话。为了达到最大的可移植性,开发者不能总假设特定客户端的所有窗口的请求都处于同一个会话中。