副标题#e#
不管正在构建的是 J2EE 照旧 J2SE 处事器应用措施,都有大概以某种方法 利用 Java Servlet —— 大概是直接地通过像 JSP 技能、Velocity 可能 WebMacro 这样的暗示层,也大概通过一个基于 servlet 的 Web 处事实现,如 Axis 可能 Glue。Servlet API 提供的一个最重要的成果是会话打点 —— 通过 HttpSession 接口举办用户状态的认证、失效和维护。
会话状态
险些每一个 Web 应用措施都有一些会话状态,这些状态有大概像记着您是否 已登录这么简朴,也大概是您的会话的更具体的汗青,如购物车的内容、以前查 询功效的缓存可能 20 页动态问卷表的完整响应汗青。因为 HTTP 协议自己是无 状态的,所以需要将会话状态存储在某处并与欣赏会话以某种方法相关联,使得 下次请求同一 Web 应用措施的页面时可以容易地获取。幸运的是,J2EE 提供了 几种打点会话状态的要领 —— 状态可以存储在数据层,用 Servlet API 的 HttpSession 接口存储在 Web 层,用有状态会话 bean 存储在 Enterprise JavaBeans(EJB)层,甚至用 cookie 可能埋没表单字段将状态存储在客户层。 不幸的是,会话状态打点不妥会带来严重的机能问题。
假如应用措施可以或许在 HttpSession 中存储用户状态,这种要领凡是比其他方 法更好。在客户端用 HTTP cookie 可能埋没表单字段存储会话状态有很大的安 全风险 —— 它将应用措施的一部门内部内容袒露给了非受信任的客户层。(一 个早期的电子商务网站将购物车内容(包罗价值)存储在埋没表单字段中,从而 可以很容易被犯科操作,让任何相识 HTML 和 HTTP 的用户可以以 0.01 美元购 买任何商品。噢)另外,利用 cookie 可能埋没表单字段很杂乱,容易堕落,并 且懦弱(假如用户克制在欣赏器中利用 cookie,那么基于 cookie 的要领就完 全不能事情)。
在 J2EE 应用措施中存储处事器端状态的其他要领是利用有状态会话 bean, 可能在数据库中存储会话状态。固然有状态会话 bean 在会话状态打点方面有更 大的机动性,可是在大概的环境下,将会话状态存储在 Web 层仍然有长处。如 果业务工具是无状态的,那么凡是可以仅仅添加更多 Web 处事器来扩展应用程 序,而不消添加更多 Web 处事器和更多 EJB 容器, 这样的本钱一般要低一些 而且容易完成。利用 HttpSession 存储会话状态的另一个长处是 Servlet API 提供了一种会话失效时通知的容易要领。在数据库中存储会话状态的本钱大概难 以遭受。
servlet 类型没有要求 servlet 容器举办某种范例的会话复制可能耐久性, 可是它发起将状态复建造为 servlet 首要 存在来由(raison d’etre) 的重要 部门,而且它对作为举办会话复制的容器提出了一些要求。会话复制可以提供大 量长处 —— 负载均衡、伸缩性、容错和高可用性。相应地,大大都 servlet 容器支持某种形式的 HttpSession 复制,可是复制的机制、设置和时间是由实 现抉择的。
HttpSession API
简朴地说, HttpSession 接口支持几种要领,servlet、JSP 页可能其他表 示层组件可以用这些要领来跨多个 HTTP 请求维护会话信息。会话绑定到特定的 用户,可是在 Web 应用措施的所有 servlet 中共享 —— 不特定于某一个 servlet。一种思量会话的有用要领是,会话像一个在会话期间存储工具的 Map —— 可以用 setAttribute 按名字存储会话属性,并用 getAttribute 提取它 们。 HttpSession 接口还包括会话保留周期要领,如 invalidate() (它通知 容器应扬弃会话)。清单 1 显示 HttpSession 接口最常用的元素:
清单 1. HttpSession API
public interface HttpSession {
Object getAttribute(String s);
Enumeration getAttributeNames();
void setAttribute(String s, Object o);
void removeAttribute(String s);
boolean isNew();
void invalidate();
void setMaxInactiveInterval(int i);
int getMaxInactiveInterval();
...
}
理论上,可以跨群集一致性地完全复制会话状态,这样群会合的所有节点都 可以处事任何请求,一个简朴的负载均衡器可以以轮询方法传送请求,避开有故 障的主机。不外,这种细密的复制有很高的机能本钱,而且难于实现,当群集接 近某一局限时,还会有伸缩性的问题。
一种更常用的方法是将负载均衡与会话相似性(affinity) 团结起来 —— 负载均衡器可以将会话与毗连相关联,并将会话中今后的请求发送给同一处事器 。有许多硬件和软件负载均衡器支持这个成果,而且这意味着只有主毗连主机和 会话需要妨碍转移到另一台处事器时才会见复制的会话信息。
复制方法
#p#分页标题#e#
复制提供了一些大概的长处,包罗可用性、容错和伸缩性。另外,有大量会 话复制的要领可用:要领的选择取决于应用措施群集的局限、复制的方针和 servlet 容器支持的复制设施。复制有机能本钱,包罗 CPU 周期(存储在会话 中的序列化工具)、网络带宽(广播更新),以及基于磁盘的方案中写入到磁盘 可能数据库的本钱。
险些所有 servlet 容器都通过存储在 HttpSession 中的序列化工具举办 HttpSession 复制,所以假如是建设一个漫衍式应用措施,该当确保只将可序列 化工具放到会话中。(一些容器对像 EJB 引用、事务上下文、尚有其他非可序 列化的 J2EE 工具范例有非凡的处理惩罚。)
#p#副标题#e#
基于 JDBC 的复制
一种会话复制的要领是序列化会话内容并将它写入数据库。这种要领相当直 观,其利益是不只会话可以妨碍转移到其他主机,并且纵然整个群集失效,会话 数据也可以生存下来。基于数据库的复制的缺点是机能本钱 —— 数据库事务是 昂贵的。固然它可以在 Web 层很好地伸缩,可是它大概在数据层发生伸缩问题 —— 假如群集增长大到必然水平,扩展数据层以容纳会话数据会很坚苦可能成 本无法接管。
基于文件的复制
基于文件的复制雷同于利用数据库存储序列化的会话,只不外是利用共享文 件处事器而不是数据库来存储会话数据。这种方法的本钱一般比利用数据库的成 本(硬件本钱、软件许可证和计较开销)低,其价钱则是靠得住性(数据库可提供 比文件系统更强的耐久化担保)。
基于内存的复制
另一种复制方法是与群会合的一个可能多个其他处事器共享序列化的会话数 据副本。复制所有会话到所有主机中提供了最大的可用性,而且负载均衡最容易 ,可是因为复制动静所耗损的每个节点的内存和网络带宽,最终会限制群集的规 模。一些应用处事器支持与“同伴(buddy)”节点的基于内存的复制,个中每 一个会话存在于主处事器上和一台(或更多)备份处事器上。这种方案比将所有 会话复制到所有处事器的伸缩性更好,可是当需要将会话妨碍转移到另一台处事 器上时会使负载均衡任务巨大化,因为它必需找出别的哪一台(几台)处事器有 这个会话。
时间思量
除了抉择如何存储复制会话数据,尚有什么时候复制数据的问题。最靠得住但 也最昂贵的要领是每次数据改变时复制它(如每次 servlet 挪用竣事)。不那 么昂贵、可是在妨碍时会有丢失一些数据的风险的要领是在每高出 N 秒时复制 数据。
与时间问题有关的问题是,是复制整个会话照旧只试尝复制会话中改变了的 属性(它包括的数据会少得多)。这些都需要在靠得住性和机能之间举办取舍。 Servlet 开拓人员该当认识到在妨碍转移时,会话状态大概变得“过期 ”(是屡次请求前的复制),并该当筹备处理惩罚不是最新的会话内容。(例 如,假如一个interview 的第 3 步发生一个会话属性,而用户在第 4 步时,请 求被妨碍转移到一个具有两次请求之前的会话状态复制的系统上,那么第 4 步 的 servlet 代码应预备在会话中找不到这个属性,并采纳相应的动作 —— 如重定向,而不是认定它会在哪里、并在找不到它时抛出一个 NullPointerException 。)
容器支持
Servlet 容器的 HttpSession 复制选项以及如何设置这些选项是各不沟通的。IBM WebSphere ®提供的复制选项是最多的,它提供了在内存中复制可能基于数据库的复制 、在 servlet 末端可能基于时间的复制时间、流传全部会话快照(JBoss 3.2 或今后版本)可能只流传改变了的属性等选择。基于内存的复制基于 JMS 宣布- 订阅,它可以复制到所有克隆、一个“同伴”复成品可能一个专门的 复制处事器。
WebLogic 还提供了一组选择,包罗内存中(利用一个同伴 复成品)、基于文件的可能基于数据库的。JBoss 与 Tomcat 可能 Jetty servlet 容器一同利用时,举办基于内存的复制,可以选择 servlet 末端可能 基于时间的复制时间,而快照选项(在 JBoss 3.2 或今后版本)是只复制改变 了的属性。Tomcat 5.0 为所有群集节点提供了基于内存的复制。另外,通过像 WADI 这样的项目,可以用 servlet 过滤机制将会话复制添加到像 Tomcat 可能 Jetty 这样的 servlet 容器中。
改造漫衍式 Web 应用措施的机能
不管抉择利用什么机制举办会话复制,可以用几种方法改造 Web 应用措施的 机能和伸缩性。首先记着,为了得到会话复制的长处,需要在陈设描写符中将 Web 应用措施标志为 distributable,并担保在会话中的所有内容都是可序列化 的。
保持会话最小
#p#分页标题#e#
因为复制会话有跟着会话中的工具图(object graph) 的变大而增加本钱, 所以该当尽大概地在会话中少安排数据。这样做会淘汰复制的序列化的开销、网 络带宽要求和磁盘要求。出格地,将共享工具存储在会话中一般不是好主意,因 为它们需要复制到它们所属的 每一个会话中。
不要绕过 setAttribute
在改变会话的属性时,要知道纵然 servlet 容器只是试图做最小的更新(只 流传改变了的属性),假如没有挪用 setAttribute ,容器也大概没有留意到已 经改变的属性。(想像在会话中有一个 Vector ,暗示购物车中的商品 —— 如 果挪用 getAttribute() 获取 Vector 、然后向它添加一些内容,而且不再次调 用 setAttribute ,容器大概不会心识到 Vector 已经改变了。)
利用细化的会话属性
对付支持最小更新的容器,可以通过将多个细化的工具而不是一个大块头放 到会话中而低落会话复制的本钱。这样,对快速改变的数据的改变也不会迫使容 器去序列化并流传慢速改变的数据。
完成后使之失效
假如知道用户完成了会话的利用(如,用户选择注销登录),确保挪用 HttpSession.invalidate() 。不然,会话将耐久化直到它失效,这会耗损内存 ,而且大概是长时间的(取决于会话超时时间)。很多 servlet 容器对可以跨 所有会话利用的内存的数量有一个限制,到达这个限制时,会序列化最先利用的 会话并将它写到磁盘上。假如知道用户利用完了会话,可以使容器不再处理惩罚它并 使它作废。
保持会话清洁
假如在会话中有大的项,而且只在会话的一部门中利用,那么当不再需要时 应删除它们。删除它们会淘汰会话复制的本钱。(这种做法雷同于利用显式 nulling 以辅佐垃圾收集器,老读者知道我一般不发起这样做,可是在这种环境 下,因为有复制,在会话中保持垃圾的本钱要高得多,因此值得以这种方法辅佐 容器。)
竣事语
通过 HttpSession 复制,Servlet 容器可以在构建复制的、高可用性的 Web 应用措施方面给您减轻许多承担。不外,对付复制有一些设置选项,每个容器都 纷歧样,复制计策的选择对付应用措施的容错、机能和伸缩性有影响。复制计策 的选择不应当是过后的 —— 您该当在构建 Web 应用措施时就思量它。而且, 必然不要健忘举办负载测试以确定应用措施的伸缩性 —— 在客户替您做之前。