|
在每篇专栏文章中,“WebSphere 反向投资者”将回答问题、提供指导和讨论与 WebSphere 产品相关的基础主题,经常会给出与流行的看法相悖的经过实践验证的建议。
知识巩固时间
在过去几周中,我花了大量的时间为技术销售专家提供有关刚发布的 IBM® WebSphere® Application Server V7 的内部培训。该培训的部分内容已分配给返璞归真的基本材料,以便为 WebSphere Application Server 新手提供背景信息,以及为老手提供知识巩固。结果证明,相当多的“老手”专家发现了我提供的实验和讲演中的价值——多得超过了我的预期。诚然:您永远无法达到不需要别人提醒您基本知识的境界。
基于同样的道理,我决定在本专栏中介绍 WebSphere Application Server 的一个基本方面:会话故障转移。更准确地说应该是:会话故障转移和我对故障转移的建议。
为什么要使用 HttpSession?
超文本传输协议(Hypertext Transfer Protocol,HTTP)是 Web 应用程序的基础,是一种无状态的协议。状态一般通过设置 Cookie 进行管理,浏览器在请求之间跟踪 Cookie,从而提供了一种将来自给定用户的一系列唯一请求关联到某个“对话”或流的机制。遗憾的是,使用 Cookie 管理状态相当困难。幸运的是,Java Servlet API 使用 HttpSession 接口提供了针对此缺点的解决方案,该接口通过会话对象在多次请求调用之间进行会话跟踪和状态管理,从而使得 Servlet 能够将给定的用户与一系列请求关联起来。
HttpSession:何时以及如何使用
遗憾的是,特定于会话的数据可以存储在会话对象中。我说的“遗憾”是因为,HttpSession 通常最终被用作应用程序缓存,这实在不是其预期的目的。虽然使用会话关联来自某个用户的请求并没有错,但是使用会话作为应用程序缓存往往容易导致许多给应用程序环境的多个方面带来压力的应用程序实践。例如,一个客户有一个 25 MB(不是印刷错误)的会话对象,他们正在将其保存到数据库,其结果是:
- 糟糕的数据库性能,因为数据库对数据库表中的该(超)大会话对象执行了数百次更新。
- 网络性能下降,因为要将更新从应用程序服务器推送到数据库服务器。
- 应用程序服务器频繁进行垃圾收集,因为原本应该用于应用程序处理的堆被与每个用户关联的会话对象消耗了;例如,200 个用户 X 25 MB = 500 MB(或 0.5 GB)大小的对话对象!即使 1GB 或 2GB 的堆对于存储这样多的应用程序状态数据来说也太“小”了。
这种对 HttpSession 的不适当使用又导致了对会话对象中的信息丢失的担心,因为重新创建存储在其中的信息是非常耗时的。
正如您可能已经猜到的,我的建议是不要将 HttpSession 用作应用程序缓存,而是将 HttpSession 用于关联多个用户请求。最好将 HttpSessions 保持尽可能小。但是在某些应用程序中,此建议未得到遵循,因此您可能需要某种“快速”方法来更改应用程序,以便在产生最小影响的情况下减少保存的内容。在此情况下,我建议使用标题为使用智能序列化提高 HttpSession 性能的文章中列出的技术来最小化会话对象中实际存储的信息量,从而在您选择采用会话分发时最小化应用程序必须处理的信息量。
会话分发:何时以及如何使用
Servlet API 为分发会话对象做好了准备。换句话说,您可以采用会话对象的副本,以便某个应用程序服务器实例的故障不会导致给定用户的应用程序状态丢失——或者更准确地说是多个用户,因为可能有多个用户使用 WebSphere Application Server 提供的 HttpSession 关联机制与某个给定的应用程序服务器实例相关联。
我的首选替代方法是不依赖会话分发,而是仅依赖 HTTP 服务器插件关联将某个用户“固定”到某个应用程序服务器,尽管这确实意味着停止应用程服务器 JVM 将导致 HttpSession 对象丢失。这样做的优点在于,当应用程序发生故障或停止时,不需要分发会话对象来为 HttpSession 对象故障转移做准备。这样做的明显不利方面在于,用户将丢失所有应用程序状态,并将需要重新登录并重新创建该状态,而这对您的应用程序或业务需求来说也许是不可接受的。我将提到我曾经与许多客户合作过,他们事实上都同意这个观点并将其作为他们的标准实践。
话说如此,也许您真的确实无法容忍某人必须重新登录并从头重新创建 HttpSession 对象。也许这是因为您遗憾地选择了使用 HttpSession 作为应用程序缓存,或者是由于其他某种原因。在任何情况下,WebSphere Application Server Network Deployment(以下称为 Network Deployment)都提供了两种用于会话分发的机制:分布式复制服务(Distributed Replication Service,DRS)和数据库持久性。我对 Network Deployment 会话分发机制的建议与在前一个专栏中陈述的建议相同,即采用数据库持久性。
另一种用于会话分发的替代机制是 WebSphere eXtreme Scale,这以前称为 WebSphere Extended Deployment 的 ObjectGrid 组件。WebSphere eXtreme Scale 提供了独立于应用程序服务器运行时的基于内存的复制机制。因此,可以将 WebSphere eXtreme Scale 用于在运行于不同应用程序服务器运行时上的不同应用程序之间共享应用程序状态(缓存)。特定于 HttpSession 来说,WebSphere eXtreme Scale 提供了一个 Servlet 筛选器,可以覆盖任何 Java Platform, Enterprise Edition EAR 文件的 HttpSession 实现。通过 WebSphere eXtreme Scale 中提供的脚本,很容易将该筛选器安装到 EAR 中。
我要补充的是,如果您事实上正在将 HttpSession 用作应用程序缓存,与 Network Deployment 中提供用于会话分发的机制相比,WebSphere eXtreme Scale 很可能是更好的替代方法。这是因为,WebSphere eXtreme Scale 旨在用作分布式应用程序缓存,而 HttpSession 从未打算用于此用途,尽管在按上述方式有效地使用 HttpSession 的情况下,Network Deployment 中的分布式会话选项是高效和可伸缩的。
HttpSession 共享呢?
与使用会话分发的决策相关的另一个决策是在 Network Deployment 单元之间共享会话。正如我在前面所讨论的,我的建议是不要这样做,因为这样增加了 Network Deployment 单元(并延伸到数据中心)之间的相互依赖性,而这是您在遭遇灾难性中断时希望避免的事情。虽然许多客户似乎接受这一点,但他们仍然坚持在数据中心的单元之间共享会话,因为他们无法正确地将某个用户与给定数据中心(或单元)中的某个应用程序服务器相关联。
幸运的是,存在对此问题的解决方案,只要您拥有适当的的基础结构和企业中所有受影响的操作实体之间的协作,您就可以在网络层应用此解决方案。如果您创建了多个独立的单元,如图 1 所示,您可以配置网络交换机(或通常所称的全局站点选择器)以正确地维护给定用户与特定单元的关联。
图 1. 具有多个独立单元的基础结构
如何实现这种配置呢?您会这样问到。当然,大多数此类设备都依赖 IP layer 2 或 IP layer 3 机制来维护从客户端到服务器的关联;这些机制的一些示例为 IP 地址散列、DNS 解析或此类性质的其他机制。如果在每个请求上一致地路由所有的传入流量,则这些类型的关联机制非常理想。但是如果通过代理服务器或其他中间设备路由流量,由于此类设备最终屏蔽了“实际”客户端 IP 地址,这些类型的关联机制将不足以确保一致地将客户端请求路由到相同的服务器(或单元)。
遗憾的是,这样的屏蔽或中转发生得太频繁了,有时跨多个请求发生在同一个给定的用户身上;一次他通过代理 A 路由,下一次通过代理 B 路由。其结果在于,网络交换机将这些请求视为不同的用户请求(即使事实并不是这样),因此,如果您没有在单元(或数据中心)之间分发会话对象,用户将丢失其应用程序状态。
解决方案依赖于在网络交换机上采用基于内容的路由,这是一种 IP layer 7 技术。此方法的关键是从 HTTP 插件使用的 plugin-cfg.xml 文件中提取每个服务器的 Server CloneID(参见下面),然后使用该 ID 为每个单元构造关联规则。然后网络交换机可以检查传入 HTTP 请求的标头,并确定应该将请求路由到哪一个单元。在下面的示例中,将构造一个规则来检查“13j9n75hm”的 HTTP 请求标头。
<Server CloneID="13j9n75hm" ConnectTimeout="0" ExtendedHandshake="false" LoadBalanceWeight="2" MaxConnections="-1"
对每个单元中的 plugin-cfg.xml 文件中的每个 CloneID 这样做,并通过路由规则将 CloneID 与某个单元相关联,可以消除在单元之间共享会话的需要——至少从这个理由上可以这样讲。
 |
重复的克隆?
虽然 Server CloneID 在单元之间应该是唯一的,但是 Network Deployment 没有办法保证这种情况的真实性。因此,您需要确保单元之间不存在重复。如果存在任何重复,只需删除并重新创建服务器即可消除重复。Server CloneID 只是应用程序服务器创建时间的散列(精确到毫秒),因此,除非您凑巧在确切的相同时间在不同的单元中创建了两个应用程序服务器,否则不应该会遇到任何重复。话虽如此,但是您始终应该核实确切,如果确实在两个不同单元中遇到了重复的 CloneID,您可能希望考虑购买彩票! |
|
当然,有人会争论说,就网络交换机开销(以及延迟)而言,实现基于内容的路由“代价太高”了。我不反对代价太高的说法,但是如果您不在前端为传入请求处理好路由,您最终将会“付出更高的代价”,不得不通过在后端添加网络容量来复制 HttpSession 数据,并且您将创建数据中心之间的依赖性,从而以其他方式损害可靠性。
如果您遇到计划外的中断,使用这里讨论的关联方法最多只会让您丢失一个会话。这是因为对于计划外的中断,您可以使用有关保持持续可用性的文章中讨论的技术“排空”一个单元,以便现有的用户保持在初始单元中,而新用户则定向到新激活的单元。从实用的角度看,如果您遇到大量的计划外中断,那么您有某些根本问题亟需解决;首先,计划外中断的根源是什么?至少,这是我将首先调查的问题。正如在可靠性工程中所述,由于在单元之间共享会话的附加复杂性,这种情况很可能会导致附加的故障,从而导致中断,这是您要在第一时间尽量避免的事情!
致谢
感谢 Keys Botzum 和 Alex Polozoff 提供的意见和建议。
|