web 复制_Web层中的状态复制

无论是构建J2EE还是J2SE服务器应用程序,都有可能以一种形式或另一种形式使用Java Servlet,即直接通过JSP技术,Velocity或WebMacro等表示层,或者通过基于Servlet的Web服务来使用Java Servlet。实现,例如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的方法将根本无法使用)。

在J2EE应用程序中存储服务器端状态的其他替代方法是使用有状态会话Bean,或将会话状态存储在数据库中。 尽管有状态会话Bean允许会话状态管理具有更大的灵活性,但在可行的情况下,将会话状态存储在Web层中仍具有优势。 如果业务对象是无状态的,则通常可以通过简单地添加更多的Web服务器,而不是添加更多的Web服务器和更多的EJB容器来扩展应用程序,这通常更便宜并且更容易实现。 使用HttpSession来存储会话状态的另一个优点是,Servlet API提供了一种简便的方法,可以在会话过期时得到通知。 在数据库中存储会话状态可能会非常昂贵。

Servlet规范没有要求Servlet容器执行任何类型的会话复制或持久化,但确实建议将状态复制从一开始就视为Servlet 生存的重要部分,并且对Servlet提出了一些要求。选择进行会话复制的容器。 会话复制可带来许多好处-负载平衡,可伸缩性,容错性和高可用性。 因此,大多数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();
    ...
}

从理论上讲,可以在整个集群上完全一致地复制会话状态,以便集群中的任何节点都可以为任何请求提供服务,而哑负载均衡器可以简单地以循环方式路由请求,从而避免出现主机故障。 但是,这种紧密复制具有相当大的性能成本和实现复杂性,并且随着群集接近特定大小,还可能存在可伸缩性问题。

一种更常见的方法是将负载平衡与会话亲缘性相结合-负载平衡器能够将连接与会话相关联,并将会话中的后续请求路由到同一服务器。 许多硬件和软件负载平衡器都支持此功能,这意味着仅当主连接主机发生故障并且需要将会话故障转移到另一台服务器时,才可以访问复制的会话信息。

复制方法

复制具有许多潜在的好处,包括可用性,容错能力和可伸缩性。 另外,有许多方法可用于会话复制。 方法的选择将取决于应用程序集群的大小,复制的目标以及servlet容器支持的复制工具。 复制具有性能成本,其中包括CPU周期(用于序列化存储在会话中的对象),网络带宽(用于传播更新),并且对于基于磁盘的方案,还包括写入磁盘或数据库的成本。

几乎所有的servlet容器执行HttpSession复制通过序列化存储中的对象HttpSession ,因此,如果你想创建一个分布式应用程序,你应该确保在会议地点只有序列化的对象。 (某些容器对诸如EJB引用,事务上下文和其他不可序列化的J2EE对象类型之类的实体进行特殊处理。)

基于JDBC的复制

会话复制的一种方法是简单地序列化会话内容并将其写入数据库。 这种方法非常简单,并且具有以下优点:会话不仅可以故障转移到任何其他主机,而且会话数据可以在整个群集的故障中幸免。 数据库支持的复制的缺点是性能成本-数据库事务非常昂贵。 尽管它在Web层中可以很好地扩展,但是它可能会在数据层中造成扩展问题-如果群集足够大,则扩展数据层以容纳会话数据量可能很困难或成本高昂。

基于文件的复制

基于文件的复制类似于使用数据库存储序列化的会话,不同之处在于,使用共享文件服务器而非数据库来存储会话数据。 与使用数据库相比,此方法通常具有较低的成本(硬件成本,软件许可和计算开销),但以某些可靠性为代价(数据库比文件系统具有更强的持久性保证)。

基于内存的复制

另一种复制方法是与集群中的一个或多个其他服务器共享序列化会话数据的副本。 将所有会话复制到所有主机可提供最大的可用性,并且在负载平衡器上最简单,但是由于每个节点上的内存消耗要求以及复制消息消耗的网络带宽,最终将对群集大小设置上限。 一些应用服务器支持基于内存的复制到“伙伴”节点,其中每个会话都在一台主服务器和一台(或多台)备份服务器上。 这样的方案比将所有会话复制到所有服务器更好地扩展,但是当负载均衡器需要将会话故障转移到另一台服务器时,由于负载均衡器必须弄清楚哪些其他服务器具有该会话,因此会使负载均衡器的工作复杂化。

时间考量

除了决定如何存储复制的会话数据外,还存在何时复制数据的问题。 最可靠,但也最昂贵的方法是每次更改数据时都复制数据(例如在每次Servlet调用结束时)。 较便宜,但是如果在故障转移的情况下存在丢失某些数据的风险,那就是每隔N秒最多复制一次数据。

与时间问题相关的问题是是否复制整个会话,还是仅尝试复制会话中已更改的属性(可能包含少得多的数据)。 这些都是可靠性和性能之间的权衡,而权衡的位置取决于您的应用程序。 Servlet开发人员应该意识到,在发生故障转移的情况下,会话状态可能是“陈旧的”(基于之前几个请求的副本),并且应该准备处理会话内容不是最新的。 (例如,如果采访中的第3步创建了会话属性,并且当用户位于第4步时,该请求将故障转移到会话状态副本为两个旧请求的系统,则应不准备第4步的Servlet代码在会话中找到该属性,并采取相应的操作(例如重定向),而不是假设该属性在那里并在不存在该属性时抛出NullPointerException 。)

容器支撑

Servlet容器在HttpSession复制的选项以及如何配置这些选项方面有所不同。 IBMWebSphere®提供了各种各样的复制选项,提供了基于内存的复制或基于数据库的复制,选择了servlet结束或基于时间的复制时间,并选择了传播整个会话快照或仅复制更改的属性。 基于内存的复制基于JMS的发布-订阅,它可以复制到所有克隆,单个“伙伴”副本或专用复制服务器。

WebLogic还提供了很多选择,包括内存(使用单个伙伴副本),基于文件或基于数据库。 当使用Tomcat或Jetty servlet容器时,JBoss会执行基于内存的复制,并选择servlet结束或基于时间的复制时间,以及一个选项(在JBoss 3.2及更高版本中),仅对更改的属性进行快照。 Tomcat 5.0向所有群集节点提供基于内存的复制。 此外,通过WADI等项目,可以通过Servlet过滤机制将会话复制添加到Tomcat或Jetty之类的servlet容器中。

改善分布式Web应用程序中的性能

无论您决定采用哪种会话复制机制,都可以通过几种方式提高Web应用程序的性能和可伸缩性。 首先,请记住,为了获得会话复制的好处,您将需要将Web应用程序标记为可在部署描述符中分发,并确保放置在会话中的所有内容都可序列化。

保持会话最少

由于复制会话的成本随会话中存储的对象图的大小而增加,因此,您应努力在会话中放入尽可能少的数据。 这样做可以减少序列化开销,网络带宽要求以及复制所需的磁盘。 特别是,将共享对象存储在会话中通常是一个坏主意,因为共享对象将在每个对象所属的会话中被复制。

不要绕过setAttribute

在会话中更改属性时,请注意,如果servlet容器尝试进行某种最小程度的更新(仅传播已更改的属性),则如果您不调用setAttribute ,则容器可能不会注意到您已经更改了属性。 (假设您在会话中有一个表示购物车中商品的Vector -如果您仅调用getAttribute()来检索Vector ,然后向其中添加一些内容,而无需再次调用setAttribute ,则该容器可能不会意识到Vector已经被已更改。)

使用细粒度的会话属性

对于支持最少更新的容器,您可以通过在会话中放置多个更细粒度的对象而不是一个大的整体对象来减少会话复制的成本。 这样,对变化较快的数据的更改也不会迫使容器序列化和传播变化较慢的数据。

完成后失效

如果您知道用户已完成会话(例如,用户已选择注销),请确保调用HttpSession.invalidate() 。 否则,会话将一直持续到它到期为止,这将消耗很长时间,这取决于会话到期超时。 许多servlet容器对可用于所有会话的内存量进行了限制,当达到该限制时,将序列化最近最少使用的会话并将其写入磁盘。 如果您知道用户已完成会话,则将工作保存到容器中并使其无效。

保持会话干净

如果会话中的任何大项目仅用于会话的一部分,则在不再需要它们时将其删除。 删除它们将减少会话复制的成本。 (这种做法类似于使用显式空值来帮助垃圾收集器,普通读者知道我一般不建议这样做,但是在这种情况下,由于复制而在会话中维护垃圾的成本高得多,因此,值得尝试以这种方式帮助容器。)

摘要

Servlet容器通过HttpSession复制,可以为您构建复制的,高度可用的Web应用程序带来很多繁重的工作。 但是,存在许多用于复制的配置选项,具体取决于容器,并且复制策略的选择对应用程序的容错性,性能和可伸缩性具有影响。 复制策略的选择不应该是事后的,您应该在构建Web应用程序时加以考虑。 而且,当然,在客户为您服务之前,请不要忘记进行负载测试以确定应用程序的可伸缩性。


翻译自: https://www.ibm.com/developerworks/java/library/j-jtp07294/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值