跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

利用 WebSphere eXtreme Scale 多主机功能改进响应时间和数据可用性

利用多主机复制特性链接和同步数据网格

Tony Efremenko, 经过认证的高级 IT 专家, IBM
Tony Efremenko 是 IBM 的经过认证的高级 IT 专家,在软件行业拥有超过 24 年的经验。
(An IBM developerWorks Contributing Author)
Web Identity Team, Web Identity Team, IBM
Web Identity Team 关注 IBM 的配置文件与安全性应用程序。他们于 2007 年确定了 Extreme scale 的用例,并长期推广应用部分。Web Identity Team 的成员包括 Fred Meredith、Brady Cooper、Snehini Sreenivasan 和 Steve Looney。

简介: 集群和负载平衡技术是使 Web 应用程序处理更加有用、更加迅速的重要最佳实践。但如果服务分布在网络上,那么这些技术就可能会导致瓶颈问题,也会使提供应用程序所用数据的服务的复杂性提高。这篇文章介绍了 WebSphere® eXtreme Scale 的多主机复制特性如何为现有 ObjectGrid 函数添加镜像功能。利用这项特性,即可将数据保存在距离需要它们的服务较近的地方,同时还可维护统一的视图。这篇文章还介绍了一种能够在多主机网格中镜像 HTTP 会话数据的变通方法。

发布日期: 2012 年 1 月 04 日
级别: 中级 原创语言: 英文
访问情况 : 477 次浏览
评论: 


概述

集群和负载平衡

下面给出了负载平衡器和集群的简短摘要说明。

希望使用负载平衡器的时机

  • 负载平衡器可将新工作发送到繁忙程度最低的服务器,从而提供更出色的整体吞吐量
  • 有多种负载平衡器产品可供选择,包括硬件设备、WebSphere Edge Server 和 Web 服务器的 WebSphere Application Server 插件
  • 负载平衡器仍然能够提供会话关联性,但一种良好的算法或者设备能够避免总是将一名用户发往同一个服务器(以避免与发生故障的服务器关联)

希望将服务器集群化的时机

  • 集群通过复制服务器实例提供了更高的可用性和吞吐量
  • 水平集群即服务器实例在不同的机器上提供
  • 垂直伸缩即将实例添加到相同的机器上(如果您有未得到利用的服务器容量,则这种做法非常有用)

集群和负载平衡是改进系统内服务的可伸缩性和可用性的有效方法。在应用于 WebSphere 等应用服务器内托管的应用程序时,这些技术能处理更高的整体吞吐量,并使整个应用程序能够实现更高的可用性。它们利用直观的方法提供了可伸缩性和可用性的改进。您是否希望提高集群解决方案的吞吐量?没有问题:只要在集群中添加一个服务器的新实例,接入负载平衡器以包含新服务器,即可实现目标。

但可伸缩系统意味着您能够调整应用程序的所有 部分,包括会话持久性和其他本地缓存数据。如果您未解决数据可用性的问题,那么就会给系统造成又一个瓶颈问题。服务器集群和负载平衡不能处理后台使用的数据。如果这些数据是访问的瓶颈问题所在,无论是由于它处于网络中距离过远的位置处,还是由于过多的连接为这些数据提供服务,都会妨碍提高应用程序处理负载的努力。如果您尝试更为简化的方法,直接通过复制来制作 “本地” 副本,那么实际上只会进一步提高复杂性。这是由于对一个副本的任何更新都必须在所有其他副本中执行,只有这样才能保持所有副本同步。

解决数据可用性问题的方法之一就是利用 WebSphere eXtreme Scale Object Grid(下文简称为 Object Grid)中称为多主机的新特性。此特性允许将您的数据镜像到另一个远程位置。这就意味着,在远程位置缓存的数据可以按需保持较近的版本,同时也能保留一份数据的同步视图。通过这种方式,即可在增加服务器数量时增加数据副本的数量,而无需担忧如何维护这些数据的单一、事务性的视图。除此之外,您也不必依靠其他产品或机制来完成这一任务,所有任务都将使用一个简单、优雅的解决方案完成。

这篇文章介绍了如何配置多主机网格,并讨论了使用多主机 7.1 版本时现存的一些限制,特别是针对 HTTP 会话持久性的限制。HTTP 会话持久性数据是数据优化的一种重要特例,对于某些类型的应用程序来说,开箱即用的功能存在一些局限之处。这篇文章介绍了作者如何与 IBM Web Identity Team 中的同事协力打造绕开这种限制的解决方案。


多主机解决方案

利用多主机,您可以在 Object Grid 中存储数据,并镜像数据来确保数据处于距离需要它的服务器较近的位置。图 1 展示了一个全局集群应用程序,其 Web 服务器和应用服务器位于纽约和香港。在 Object Grid 的纽约节点副本中作出的更新将通过多主机特性传播到香港的节点。对于登录到香港节点的用户来说,如果因某种原因负载平衡器将其路由至纽约节点,用户不会发现数据展示存在任何差异。如果在纽约服务器中进行数据更新,那么用户在使用香港服务器时仍然可以使用这些更新,并且可将更新后的数据作为香港服务器中的本地数据。


图 1. 多主机数据镜像
多主机数据镜像

Object Grid 使用经过优化的内部事务功能来执行镜像。您不需要为执行镜像的方式配置或维护内部组件。要利用镜像特性,只需使用标准 Object Grid 应用程序编程接口即可。产品将替您维护数据副本。对于一个网格镜像的更新将自动传播到其多主机副本。


如何配置多主机网格

配置多主机非常简单,只需要在 WebSphere eXtreme Scale 7.1 或更新版本的对象网格配置中使用一个简单的属性文件设置即可。安装了对象网格安装文件(一个 JSE 安装文件)之后,定义本地和域名,随后链接端点即可。就是这样简单。步骤如下:

  1. 确保服务器了解 server.properties 文件的位置。因此在产品提供的 Object Grid 启动脚本中,确定 serverProps 文件的路径,如下所示:-serverProps /usr/WebSphere/eXtremeScale/ObjectGrid/bin/server.properties
  2. 在 server.properties 文件中定义本地域名:domainName = Local
  3. 在 server.properties 文件中定义外部域:foreignDomains= Foreign
  4. 在 server.properties 文件中为外部域提供可选端点列表:Foreign.endpoints=host1:2809,host2:2809

当前限制

在使用多主机解决方案时,目前存在以下限制:

  • 网格名称和映射集名称必须匹配
  • 必须使用固定分区,必须有相同数量的分区
  • 相同的数据类型和模板
  • 部分加载器限制适用

与多主机带来的价值相比,大多数此类限制都是微不足道的。然而,目前有一项限制使多主机无法开箱即用地用于 HTTP 会话持久性:必须使用固定分区,每一个多主机副本中的分区数量都必须相同

Object Grid 实际上可不做任何修改地用于提供会话持久性(下文将介绍更多内容)。它使用分区算法来执行一些内部管理功能,因此它以容器为单位提供优化的 HTTP 会话持久性,而不是以固定分区为单位。对于某些应用程序来说,这可能已经足够了,但在许多用例中,都值得编写一点代码来实现多主机网格的全部优势。这些优势包括:

  • 您可以通过已知关键字直接访问数据
  • 您可以预测希望在网格中保存多少数据,因此可以使用固定分区

对于 HTTP 会话数据,只要完成两项任务,即可满足获得这些优势。

  • 使用 SessionID 和应用程序上下文作为关键字
  • 理解峰值用户负载,以便预测最大网格大小(实际上,就是根据峰值负载确定网格的规模)

处理 HTTP 会话数据是许多需要数据镜像的应用程序的一项关键要求。本文的后续部分将讨论利用 Web 容器的标准 J2EE 扩展来绕开这些限制的一种方法。


特殊情况:如何绕开自动会话持久性方面的限制

请注意,您可以通过简单的编码来绕过这项限制,即直接利用网格 API 在多主机网格中存储数据。这需要检查使用 setAttributegetAttribute 的位置,并重载它们来包含简单的网格 API。这种方法并无错误,也非常易于实现。

但如果您希望采用一种更加通用的方法。假设您希望使用应用程序现有的 getAttributesetAttribute API,不希望做出任何修改(也就是说,不希望编写代码来覆盖这些方法),也不希望检查代码,将 Object API 添加到需要使用它的位置。在这样的时候,您可以通过一些简单的添加来扩展您的能力,而无需修改现有应用程序。需要编码的内容如下:

  • 一个新的 Servlet 过滤器:MySessionOverrideFilter
  • 一个新的 Servlet 请求包装器:MyHttpServletRequestWrapper
  • 一个新的会话属性侦听器:MySessionAttributeListener

可以轻而易举地连接这些内容,提供所需功能,而且无需进一步更改底层应用程序。所有这些功能都是一种标准 Java Enterprise Edition 产品的一部分 - 这种方法不存在根本性的更改,不存在不受支持的部分。实际上,您可以立即使用这些工具获得其他功能。

新模块编程流程说明

图 2 展示了模块彼此交互的流程。


图 2. 展示组件的设计概览
展示组件的设计概览

下面给出了这些模块彼此交互的流程:

  1. 浏览器发出应用程序请求
  2. WebSphere 容器在运行 servlet 或 JSP 之前调用过滤器类 MySessionOverrideFilter。过滤器例程使用 MyHTTPServletRequestWrapper 覆盖 HTTPServletRequest,这将覆盖 getSession() 方法。
  3. 调用应用程序 servlet。通过 setAttribute() 方法对属性做出的任何更改都将触发 WISessionAttributeListener listener.logic 内的 attribute 方法
  4. 如果会话是新会话,则覆盖 session.getSession() 方法,使之使用数据的 Object Grid 版本

对于所有对象,网格中存储的类都必须可序列化,Object Grid 必须理解对象签名。可序列化接口的要求之一就是类签名必须为已知,对象必须实现无参数的构造函数。为实现这种应用程序类的序列化,您可能需要将应用程序 JAR 文件添加到一个对象网格服务器的 –classpath 参数中。


变通方法的实现细节

覆盖过滤器链:MySessionOverrideFilter

一个新过滤器将添加到应用程序的 sessionManager.war 文件中。其目的在于覆盖常规 HTTP 处理的过滤器链,转为调用一个新的 servlet 包装器。新 servlet 包装器将用于覆盖 HTTPSession.getSession() 方法。

可以使用标准 Rational® Application Developer 向导(图 3)添加过滤器。(突出显示 WAR 文件、右键单击并选择 New->Filter。)


图 3. 用于创建新 servlet 过滤器的 Rational 向导
用于创建新 servlet 过滤器的 Rational 向导

清单 1 展示了新过滤器的主要方法。


清单 1. 过滤器覆盖的代码片段
				

public void doFilter(ServletRequest request, 
ServletResponse response,
			FilterChain chain) throws 
java.io.IOException, ServletException {
		
		// Instantiate MyHttpServletRequest to override "getSession"
		chain.doFilter(new
                  
MyHttpServletRequestWrapper((HttpServletRequest)request),
				response);
	}

chain.doFilter 方法将覆盖常规过滤器链,转为调用客户包装器 WIHttpServletReqeuestWrapper

为了确保过滤器已应用于整个 web 应用程序,可将过滤器映射添加到 web.xml 中,如清单 2 所示。


清单 2. 过滤器映射 XML
				
<filter-mapping>
		<filter-name>MySessionOverrideFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

使用包装器读取 Object Grid 中的属性:MyHttpServletRequestWrapper

作为 Servlet 2.3 规范的一部分,新增的 HTTPServletRequestWrapper 接口允许您覆盖某些 web 容器方法。在这样的设计中,HTTPServletRequest getSession() 方法将被覆盖,以便读取 Object Grid 中的属性。

清单 3 包含强调了这种解决方案关键部分的代码片段。


清单 3. 覆盖 getSession() 方法的代码片段
				

public class MyHttpServletRequestWrapper extends 
HttpServletRequestWrapper {
	private static String username =  "user";
	private static String password = ""; 

	@Override
	public HttpSession getSession(boolean aCreateIfAbsent) 
{
	
// Still get the session from the container…
		httpSess = super.getSession();

		sessionId = httpSess.getId();
	

		try {
			// Get a session with the grid
			gridSess = grid.getSession();

CODE OMITTED…
		
			// Get the ObjectMap
			map1 = gridSess.getMap("test");
			// ObjectMap map2 = sess2.getMap("test");
			gridSess.begin();
			System.out
			gridStoreObj = (Hashtable) 
map1.get(sessionId);
			
CODE OMITTED…
			// set attributes for each object in the grid
			Iterator iter = (Iterator)
 gridStoreObj.keySet().iterator();
			while (iter.hasNext()) {
				String name = (String) iter.next();

				Object value = gridStoreObj.get(name);
CODE OMITTED…

				httpSess.setAttribute(name, value);
				return httpSess;
	}

新的 MyHTTPServletRequestWrapper 覆盖了 HTTPServletRequest,这就是按照 Servlet 2.3 规范覆盖默认行为的正确接口。

模块从超类中获取会话,并检查该会话的会话 ID 是否能在 Object Grid 的本地副本中找到。如果能,则使用标准 setAttribute 方法,将会话的 Object Grid 版本的全部属性应用于会话对象的内存版本(httpSess 变量)。

这里的一个要点是:如果 setAttribute 也被覆盖为实现网格持久性,则存在连续递归调用造成循环逻辑 bug 的风险。为了避免这种问题,我们将编写会话属性侦听器来避免此类调用。请参见清单 3 中的 MySessionAttributeListener 代码,了解如何编写这种侦听器。

另外一种替代性的解决方法就是确保这是唯一出现容器 setAttribute 方法的位置,而应用程序的其他部分使用新方法来设置属性。

添加一个会话属性侦听器:MySessionAttributeListener

可以通过 Rational 中的向导添加一个会话属性侦听器,以便侦听属性更改。可以使用标准 Rational 向导添加此类(图 4)。


图 4. 创建会话属性侦听器的 Rational 向导
创建会话属性侦听器的 Rational 向导

清单 4 突出显示了 MySessionAttributeListener 的关键逻辑。


清单 4. 会话属性侦听器代码片段
				

public class MySessionAttributeListener implements 
HttpSessionAttributeListener,	HttpSessionBindingListener, 
HttpSessionListener,
		HttpSessionActivationListener {

	/**
	 * @see 
HttpSessionAttributeListener#attributeAdded(HttpSessionBindingEvent)
	 */
	public void attributeAdded(HttpSessionBindingEvent se) {
		if (thisClassCalledMe("WIHttpServletRequestWrapper")) {
			// do nothing if called by the wrapper to avoid recursive call

           } else {
			try {
				// Get a session with the grid
				gridSess = grid.getSession();

				map1 = gridSess.getMap("test");
				// ObjectMap map2 = sess2.getMap("test");

				gridSess.begin();
				storeObj = (Hashtable) map1.get(sessionId);
				gridSess.commit();
			} catch (Exception e) {
				e.printStackTrace();
			}
			// if nothing in grid, stick regular session there..
			// if (storedSession == null) {
			if (storeObj == null) {
				try {
					storeObj = new Hashtable();

					storeObj.put(se.getName(), se.getValue());
					gridSess.begin();
					// map1.insert(sessionId, currentSession); 
                    //
					// currentSession);
					map1.insert(sessionId, storeObj); 
                    // currentSession);
					gridSess.commit();

				} catch (Exception e) {
					try {
						gridSess.rollback();
					} catch (Exception e2) {
						e2.printStackTrace();
					}
				}

				// if something in grid, use it...
			} else {
				storeObj.put(se.getName(), se.getValue());
				try {


					gridSess.begin();
					// add the new attribute
					map1.update(sessionId, storeObj); // 
					gridSess.commit();
				} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}

会话属性侦听器提供了三种方法来检测 HTTP 会话名称/值对的更改。这些方法是:

  • attributeRemoved(HTTPSessionBindingEvent)
  • attributeAdded(HTTPSessionBindingEvent)
  • AttributeReplaced(HTTPSessionBindingEvent)

代码片段关注的是 attributeAdded(HTTPSessionBindingEvent) 方法。调用 API session.setAttribute(name, value) 时将调用此方法。

在类中,使用 thisClassCalledMe 方法来评估调用堆栈。如果调用了特殊 WIHTTPServletRequestWrapper 类,则忽略在网格中保留属性的逻辑,因为此时网格中应该已经具备了这些属性。随后,逻辑将使用常规 Object Grid get/put API 来更新 Object Grid 中的数据。

清单 5 展示了完整的 thisClassCalledMe 方法。有关此方法的更多细节,请参见 参考资料 部分。


清单 5. 评估调用堆栈的代码片段
				
public static boolean thisClassCalledMe(String who) {
		StackTraceElement[] stackTraceElements = 
Thread.currentThread()
				.getStackTrace();
		for (int i = 5; i < stackTraceElements.length; 
i++) {
			StackTraceElement ste = 
stackTraceElements[i];
			String classname = ste.getClassName();
			if (classname.equals(who)) {
				return true;
			}
			String methodName = ste.getMethodName();
			int lineNumber = ste.getLineNumber();
		}
		return false;
	}

它使用线程上下文和 getStackTrace 方法确定调用方是否是特定方法,从而对递归调用进行有条件的覆盖调用。


特殊情况与其他 HTTP 会话持久性方法的对比

毫无疑问,会话持久性的特殊情况要求已经存在了较长的时间,而其他方法确实提供了一些具有吸引力的特性。下面简要介绍了这些方法及其与上述方法的不同之处:

  • 数据库会话持久性。在数据库会话持久性中,使用一个中央数据库来存储会话数据。数据库会话持久性是一种常用、可靠的方法。WebSphere Application Server 将其作为一种便捷的选项提供,可通过最少的配置加以利用。它提供了数据的单一视图,数据库高可用性技术消除了数据库成为单一故障点的问题。IBM DB2 数据库也提供了队列复制和高可用性数据库复制,从而提供数据复制特性。
  • 内存到内存 (M2M) 会话持久性。WebSphere Application Server 还提供了内存到内存 (M2M) 会话持久性。在这种方法中,应用服务器将会话更新的副本推送到其他 WebSphere Application Server 实例。M2M 解决方案有着集群化的设计,因此提供了内置的容错能力。它们还能将数据存储在距离使用这些数据的服务器较近的位置。在这里,应该倍加谨慎,确保设置复制路径,避免单独一个服务器成为单一故障点,避免出现过多的跨网络流量。
  • WebSphere eXtreme Scale 或 XC10 提供的会话持久性。当然,WebSphere eXtreme Scale 提供了自己的会话持久性功能。它能在通用计算机或采用专业设备技术的 XC10 设备上实现。它提供了内置的容错能力,并且易于安装。(它是作为最新版本的 WebSphere 的一项配置的一部分提供的。)但务必牢记,这种方法难以控制网格内部组件的数据放置。其内部算法控制数据放置,因此部分数据将仍然距离使用这些数据的服务器较远。当然,目前它还没有使用多主机技术。

结束语

可以看到,“上扩” Web 应用程序的常用技术可能会带来数据可用性方面的问题。通过其他方法可以解决该问题,但会很快造成复杂度升高。解决数据可用性问题的一种新方法就是 WebSphere eXtreme Scale 7.1 版本中的多主机特性。多主机复制提供了制作数据镜像副本的一种方法,且无需考虑制作镜像的机制。如果您需要扩展其功能,包含 HTTP 会话数据,也可以按部就班地完成该任务。eXtreme Scale 可靠、事务性的设计使解决方案切实可行,而其中包含的简单步骤又使之成为满足数据可用性需求的便捷选择。


致谢

除了 Web Identity Team 之外,作者还要感谢 Object Grid 团队的以下成员:Billy Newport 和 Doug Berg。



下载

描述名字大小下载方法
多主机解决方案示例代码Multi_Master_JEE_PI.zip22KBHTTP

关于下载方法的信息


参考资料

学习

获得产品和技术

讨论

作者简介

developerWorks 投稿作者

Tony Efremenko 是 IBM 的经过认证的高级 IT 专家,在软件行业拥有超过 24 年的经验。

Web Identity Team 关注 IBM 的配置文件与安全性应用程序。他们于 2007 年确定了 Extreme scale 的用例,并长期推广应用部分。Web Identity Team 的成员包括 Fred Meredith、Brady Cooper、Snehini Sreenivasan 和 Steve Looney。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=783892
ArticleTitle=利用 WebSphere eXtreme Scale 多主机功能改进响应时间和数据可用性
publish-date=01042012

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。