【首页】 【源码】 【文章】 【外包】 【阿蒙博客】 【相册】 【论坛】 【关于】

所属分类: 技术文章 更新日期:2005-12-29 11:44:31 阅读次数:2161

分布式组件和服务的性能模式



Web 服务和其他远程接口设计指南
级别: 初级
AndrÉ Fachat
IT 架构师, IBM Global Services
2004 年 8 月
交互式组件和服务网络中包括越多的应用程序,就越需要考虑潜在的性能问题,这通常是由分布式体系结构而引起的。某一特定功能或方法的执行时间当然还是很重要的,但就分布式组件对性能问题的影响而言,他们之间的通信时间现在受到了牵连。
Andre Fachat 通过一个购物车实例讨论了分布式设计。该实例非常简单,但是也强有力的证明了什么是好的做法,什么是不好的做法。实例显示了通过减少远程调用的数量来保持通讯的粗粒度是一个很好的办法。它还提出了 ID-Lists模式,把多个远程调用合并为一个。
引言
实现分布式系统通常包括通过网络来集成分布式应用程序,在这个网络中,每个应用程序提供一组特定的服务。这就引出了面向服务的体系结构 (SOA) 的概念,例如,在 [ SOA, SOAD] 中有介绍。假设随着新的远程访问规范的发布,并且各种产品都支持它,和旧的应用程序一样,IT 架构师需要为在单个应用程序中进行远程访问处理大量不同类型的规范。这些服务可以作为远程企业 JavaBean (EJB) 方法调用、Web 服务或 CORBA 调用(这里只列出几个)来执行。本文尽量保持通常的体系结构观点,而不是就任何具体的实现技术而言的。
调用远程服务包括将请求数据打包成在服务请求方可传输的格式,把数据传送给服务,然后在服务响应方将请求解包。对于响应这个过程刚好相反。因此,远程实现功能的感知响应时间不仅仅取决于远程实现的性能,还依赖于:
• 完成一个特定的功能需要单独远程调用的数量
• 建立远程调用的时间
• 打包和解包请求和应答所需的时间
• 将请求传送给服务并返回响应所需的时间(这个时间取决于传输延迟、传输速度以及数据量)
为远程访问设计接口时必须牢记它是否影响整体性能。需要注意的是,完成一个特定的功能可能需要调用多个远程服务。
通常需要重复使用现有的服务。记住,那些服务可能是用本地应用程序设计和开发的,不能用于远程访问。我曾经历过这样一个情况,在企业资源计划 (ERP) 系统中,购物车里条目的价钱逐个地计算。当我们要执行的基于 Web 的商店调用该价格函数时,由于许多远程调用请求该 ERP 系统,性能非常糟糕。我们不得不在 ERP 系统中执行一个封装器,用来计算所有条目的价钱,然后从我们的 Web 商店调用封装器来提高性能。
在下面的部分中,我将介绍前面提到过的那个简单实例,它读取一个购物车并进行分析,找出提高性能的模式。在第 2 部分中,我将提供一些具体的模式。
实例用例
该实例讨论了从远程系统中读取购物车的用例。可以在基于 Web 的商店的上下文中看到该实例,在实例中,客户端应用程序代表用户与分布式系统进行交互,把条目集中放到购物车中,最后购买所选的条目。用例中读取的数据应该用来向用户显示购物车。购物车由消息头(显示送货地址和付款信息)和一列条目组成,每个条目包括一个商品和该商品的数量。
图 1 显示了对象模型。该模型由一个购物车对象 (“ Cart ”) 组成,这个对象包含了购物车消息头信息。 Cart 可以有多个条目, CartItem 对象包含对 Article 对象的引用以及购物车(为了更清楚地说明问题,图中故意省略了属性)中该商品的数量。 Article 对象包含关于商品的通常信息(例如,价格、重量、图片), ArticleDescription 对象包含关于该商品的详细语言描述。为以后扩展该系统还附带了一张 Auction 表。
图 1.购物车对象模型。

读取购物车的用例包括读取 Cart 和 CartItems ,就象商品信息包括 Article 和 ArticleDescription 对象一样。因此,设计的时候就应该考虑到以后会有交易,而交易会涉及到商品,而且商品信息应该从外部 ERP 系统中读取。
在下面的部分中,我将讨论当用户(由客户端应用程序代表)与远程系统交互时,这个用例的实现的几个方面。第一个实例的实现使用的是最初的设计方法,由于是按标准使用模式提出的,表明它不是最佳的访问模式,因此会引起性能问题。
远程实体 bean
企业 JavaBean 组件广泛用于远程访问数据和服务。实体 EJB 组件的主要目的是远程访问持续数据,这些数据主要存储在关系数据库中。在购物车用例实例中,您必须假设对于每个对象您都有一张表,这些对象是指: Cart 、 CartItem 、 Article 和 ArticleDescription 。对每张表定义一个实体 bean。客户端应用程序通过 Java 命名和目录接口 (Java Naming and Directory Interface,JNDI) 服务来查找 Home 接口,然后在 Home 接口上可以用 finder 方法检索 bean 的实例。有关那四张表的初始 Home 接口请参阅清单 1:
清单 1.对象表的 Home 接口

public interface CartHome extends javax.ejb.EJBHome {

Cart create() throws javax.ejb.CreateException;

Cart findByPrimaryKey(CartId id) throws javax.ejb.FinderException;
}

public interface CartItemHome extends javax.ejb.EJBHome {

CartItem create() throws javax.ejb.CreateException;

CartItem findByPrimaryKey(CartItemId id) throws javax.ejb.FinderException;

Collection findByCart(CartId id) throws javax.ejb.FinderException;
}

public interface ArticleHome extends javax.ejb.EJBHome {

Article create() throws javax.ejb.CreateException;

Article findByPrimaryKey(ArticleId id) throws javax.ejb.FinderException;
}

public interface ArticleDescriptionHome extends javax.ejb.EJBHome {

ArticleDescription create() throws javax.ejb.CreateException;

ArticleDescription findByPrimaryKey(ArticleDescriptionKey id) throws javax.ejb.FinderException;

Collection findByArticle(ArticleId id) throws javax.ejb.FinderException;
}
请注意 CartItemHome 接口有一个 finder 方法,该方法为特定的购物车查找所有的条目。同样地, ArticleDescriptionHome 也有一个 finder 方法,用来查找特定商品的所有描述。
要执行该用例,即读取购物车,通过调用 findByPrimaryKey(CartId) 来调用购物车的 finder 方法。该方法返回一个用于购物车表的列的实体 bean,这个实体 bean 有一个给定的 CartId 。然后调用 CartItemHome.findByCart(CartId) 用给定的 CartId 为属于购物车的条目检索 CartItem bean。每个 CartItem 包含数量和一个 ArticleId 。要读取该商品数据,必需 ArticleHome 接口,并且为购物车中的每个商品调用该接口的 findByPrimaryKey() 方法。检索到的 bean 中包含显示购物车所必需的信息。
但是,为什么这样使用实体 bean 不是一个好方法?这是有原因的:在 bean 实例上对任何方法的调用都是使用远程调用,从服务器上检索数据,或把数据写到服务器上。显然,这些远程调用在性能上需要很大的代价,因此,通常都雇用其他的机制。我们的实例使用的是传输值对象的会话层 (用 JavaBean 组件来传送表中的数据,[ TVO]),而不是直接调用实体 bean,应用程序调用一个会话层 bean [ TopJ2EE],该 bean 从数据库中读取数据,用读取的值填充 JavaBean 组件,然后把组件返回给调用方。写入数据库时情形是类似的,在本实例中,调用方用将要写入数据库的数据填充传输值对象,在会话 bean 上调用 write 方法,然后执行 write 功能。例如,IBM 在它的 AccessBeans 组件中压缩了这个模式,而 WebSphere® Studio 系列产品 [ AB] 都用到了 AccessBeans 组件。现在,使用各种访问 bean 模式被认为是一种艺术,我假设这个模式适用于所有的后继实例。因此,我只关注检索传输对象的方法。
一点理论
这个基于 EJB 的实例显示了一个当访问远程组件和服务时提高性能的重要模式,即减少远程调用的数量,但是为什么远程调用需要这么高的代价呢?在某种程度上理解这一点可以帮助避免性能问题。
在下面的部分中,我将研究从服务请求方发送消息到服务响应方。假设该消息由信息头部分和一些条目组成,这些条目大小相同,但数量不定。这个方法在算术上最接近大小不定的消息(请原谅,我将尽量使它简短)。
图 2:远程调用的上半时段

图 2 显示了发送请求信息到远程服务的不同阶段:
1. 构建远程调用(例如查找 Home 接口,构建编组程序(marshaller)等。)
2. 将请求数据打包,即把请求转化成适合在网络上传输的格式(例如,采用 Java 技术将请求转化为用于 J2EE SOAP 的 XML,用于 IIOP 的 Java 二进制对象)。
3. 将打包后的数据传输到远程终端。
4. 构建接收端(构建分组程序(unmarshaller),例如,XML 解析器等。)
5. 将请求解包成对象,用来调用远程服务实现。
请注意上图中只显示了一些必需的阶段,这些阶段中不包括本地调用。
上图显示了进程中的有些阶段依赖于条目的数量,也就是说,依赖于消息的大小,而其他的阶段则不依赖于条目数量:
T_setupTx
在上面的实例中,设置发送器的时间不依赖于条目的数量。
T_marshall = T_marshall_hdr + N * T_marshall_item
在上面的实例中,打包请求的时间依赖于条目的数量。
T_transport = T_latency + T_transport_hdr + N * T_transport_item
T_transport_hdr 和 T_transport_item 间的时间间隔依赖于网络带宽(字节/秒)和各自数据的大小(#字节)。 T_latency 是把发送方的第一个字节放在传输媒体上和在接收方接收该字节所消耗的时间。
T_setupRx
在上面的实例中,设置接收器的时间不依赖于条目的数量。
T_unmarshall = T_unmarshall_hdr + N * T_unmarshall_item
解包请求的时间再次依赖于条目的数量。
稍稍重新分解一下公式,调用所需要的总时间 T_total 变为:

T_total = T_setupTx + T_marshall_hdr + T_latency
+ T_transport_hdr
+ T_setupRx + T_unmarshall_hdr
+ N * (T_marshall_item + T_transport_item
+ T_unmarshall_item)
该表达式也可以写为:

T_total = T_fixed + N * T_item
将固定部分合并:

T_fixed = T_setupTx + T_marshall_hdr + T_latency
+ T_transport_hdr + T_setupRx + T_unmarshall_hdr
将不定部分合并:

T_item = T_marshall_item + T_transport_item + T_unmarshall_item.
值得注意的是远程调用的总时间依赖于大量不同部分。当估算远程调用的时间时,例如,用跟踪语句,了解每一部分从哪里开始以及到哪里结束非常重要,因为这样才可以在适当的位置设置跟踪语句。用不同的消息大小来测算远程调用也很重要,这样可以检测性能问题是由固定部分引起的还是由依赖于消息大小的不定部分引起的(消息大小指的是条目的数量)。
高速缓存 JDBC 连接(当然,远程数据库调用也是一种远程服务)就是一个实例,在这个例子中发现了上面那样的问题并得到了解决。高速缓存连接通过避免与数据库连接的时间(除了第一次)减少了设置时间。基于相同的道理,从 JNDI 检索到的 EJB Home 接口也被高速缓存了。
公式也显示了减少远程调用数量的主要原因。用 n个远程调用传输相同数量的条目时,需要花费的时间为:

T_multi = n * T_fixed + n * T_item
但是在一个调用中传输相同数量的条目需要花费的时间为:

T_single = T_fixed + n * T_item.
两种传输的差别在于 (n-1)*T_fixed 。由于 T_fixed 包含网络延迟,而网络延迟通常只有用新硬件才可以优化(如果是从根本上进行优化的话),您会发现把多个调用合并为一个调用 [ FLDD] 很容易减少远程调用的数量。
适当的指导
在本文中的基于 Web 的商店同时也是基于 EJB 的实例中,我找出了影响分布式远程调用性能的相关因素。在这个由固定和不定因素组成的方程中有三个主要影响方面:打包、传输和解包过程中的数据。在应用程序发送方和接收方上的初始化过程和网络上的延迟进一步影响了这些方面。这一部分讲述了一些原理,第 2 部分我将举一些实例。我还要用到这里描述的样本用例,通过减少远程调用的数量优化实例的性能。在这个过程中,我将讨论远程访问模式和分离相关因素以及高速缓存,最后就如何进行好的分布式应用程序设计提供适当的指导。
致谢
感谢 Stefan Peuser, Olaf Zimmermann, Witold Szczeponik 以及 Frank MÜller 为本文提出宝贵的修改意见。
参考资料
• 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
• [ SOA] Mark Colan,“ 面向服务的体系结构扩展了 Web 服务,第 1 部分 - 面向服务体系结构的特点” ( developerWorks,2004 年 4 月)
• [ SOAD] Olaf Zimmermann,Pal Krogdahl,Clive Gee,“ 面向服务分析与设计原理 - 用于 SOA 解决方案的各学科间的建模方法”( developerWorks,2004 年 6 月)
• [ TopJ2EE] Kyle Brown,Keys Botzum,Ruth Willenborg,“ 开发者技术期刊:10 个(或多或少)J2EE 最佳实践”( developerWorks,2004 年 5 月)
• [ DP] Erich Gamma,Richard Helm,Ralph Johnson 和 John Vlissides, 设计模式:可复用面向对象软件原理 ,ISBN 0201633612,Addison-Wesley Pub Co。
• [ SoC1] Edsger W. Dijkstra,“ Separation of Concerns”


--------------------------------------------------------------------------------
相关文章
基于中间件技术的多层分布式系统的研究 2005-12-29 11:47:07
基于.Net Framework的N层分布式应用开发 2005-12-29 11:45:40
分布式网络考试系统原型分析及实现 2005-12-29 11:43:38
分布式多层数据库开发 2005-12-29 11:42:05
多层结构、非分布式、分布式 2005-12-29 11:40:52


文章分类

评论文章
软件工程
文学艺术
娱乐文章
其他文章
技术文章
最新新闻

本类热门文章

  亲密接触VC6.0编译器(5673)
Asp.Net 连接Or...(5086)
驯服ActiveX控件(4803)
利用C++语言设计可扩展...(3647)
微软公司软件开发模式简介(3556)
用友华表Cell报表面面观(3545)
Cell组件---实现报...(3487)
Cell报表,软件开发人...(3469)
Cell组件在伊春市林区...(3461)
用友华表Cell报表工具...(3203)
分布式网络考试系统原型分...(3198)
分布式多层数据库开发(3039)
ASP学习:错误8000...(2996)
基于中间件技术的多层分布...(2948)
用ADO.NET的最佳实践(2880)

文章搜索



其它文章

本站首页  -  网站导航  -  广告服务  -  流量分析-  联系阿蒙
感谢 -->珠海市发思特软件技术有限公司<-- 提供空间
Copyright 2002 www.vchome.net All rights reserved
阿蒙工作室    版权所有