级别: 初级 David A. Kra (dakra@us.ibm.com), 执行 IT 架构师
2004 年 6 月 01 日 本文是该系列文章的第 3 部分,文章讲述了六种启用网格应用的策略中的第三种和第四种。对于第三种策略来说,批处理程序的工作经过划分,这样多个独立的程序实例可以并行处理作业的每一部分。对于第四种策略而言,程序变成了服务子例程,客户机可以通过某种网格中间件来调用这个例程。主要目标是使应用程序尽可能不知道中间件的存在。
简介
本系列的第一篇文章
启用网格应用的六种策略,第 1 部分,概述概要介绍了六种策略,并总结了每一种策略的特性和带来的益处。
第二篇文章
启用网格应用的六种策略,第 2 部分:策略 1:简单批处理和策略 2:独立并发批处理。向我们展示了如何用前两种策略启用网格应用。在第一种策略中,应用程序作为一项作业,在多台网格计算机中的任意一台上运行。
本文是这个系列的第三篇文章,文中讨论了用策略 3:并行批处理和策略 4:服务来启用网格应用的情况。请注意,这两种策略是相互排斥的,也就是说,您只能采用两种策略中的一种,这一点很重要。稍后,您可以考虑用策略 5 提供更多的网格功能,不过从策略 3 和策略 4 都可以发展到策略 5。有关这六种策略的说明,请参见
图 1。
图 1. 六种策略
策略 3:并行批处理
您可以看到,通过使用策略 2:独立并发批处理,我们可以运行多个实例,其中每个实例都服务于一名不同的用户。
然而,在策略 3:并行批处理中,批处理任务经过了划分。它包含多个独立的子作业,可以根据提交总作业的用户的指示并发地运行。
策略 3 提供了以下的特性:
- 程序的功能可以划分为一个客户机和多个服务器作业。
- 客户机可将整个作业划分为更小的子作业,以便在服务器之间分发。
- 网格将提交给其节点的任务分散开来。
- 服务器作业作为多个独立的实例运行。
- 运行结果聚合完毕之后,客户机可以将结果组装起来。
图 2 说明了如何在节点中划分子作业的情况。
图 2. 划分子作业
对策略 3 而言,基本的案例分析是一个可输入命令行参数的程序,然后,这个程序会使用在命令行参数中指定的文件和数据库。这种策略已经具备了用策略 1 和策略 2 启用网格应用时所要求的属性。用“客户机”和“服务器”这两个术语来讲,这个程序就是服务器上的子作业程序,由某个客户机提交以便执行。请下面的参见图 3。这个程序也可以具有许可证。
图 3. 网格环境中的客户机和服务器
服务器处理、客户机和结果聚合
用策略 3 启用网格应用时应该关注三个方面:
-
由服务器程序来完成业务应用程序的功能。
- 客户机程序将工作划分为子作业。
- 客户机程序负责聚合结果。
这种策略使服务器端业务程序避免了指定将要运行的特定网格中间件。
用策略 3:并行批处理启用网格应用带来的好处
策略 3 的目标是能够完成下面的工作:
- 在多台机器分布更小的工作单元。
- 完成整个工作的时间更快。
-
对子任务单元或单台机器失败的恢复能力更强。如果子任务单元失败,只需重新运行这项子任务,而无需运行整个作业。
划分、处理和结果聚合
除非是显而易见的情况,否则必须提供一个例子,说明如何对客户机程序进行划分,然后由其他程序进行后续的分发工作。这里所说的其他程序是指由运行小组或部署小组所选择的网格中间件。然后,集成小组和部署小组会根据您提供的例子对所选的运行环境进行调整。
您必须对服务器上的业务功能程序及其参数进行调整,使之能够在每次运行的时候处理作业中的一部分工作。您可以对使用网格的算法进行优化。比如说,您可以对算法进行适当的修改,使之在一台机器上运行的时候花费的时间较长,但是当在多台机器上运行时,性能按照比例提升。
除非聚合结果的工作简单到只需按任意顺序把输出文件连接在一起,否则您必须提供如何聚合结果的客户机程序示例。
客户机
您必须提供一个或一组客户机程序和 shell 脚本,实现下列功能:
- 将需要完成的整个工作划分成多个子单元,这些单元可由网格中间件分发到处理节点上。子单元的数目可能比网格中的工作节点数多很多(大约十倍)。
-
告诉网格中间件子单元是什么,并且对于每一个子单元,都必须运行一次主服务器的程序。
如果需要进行结果聚合,那么同一段 shell 脚本在客户机上运行的时候还要完成下面的工作:
- 调用网格中间件收集结果。
- 调用从子单元结果中收集结果的样例程序。
部署应用程序的人将会对这些样例程序和 shell 脚本进行修改,使之适应目标环境。
网格中间件
网格中间件将完成下列功能:
- 将工作子单元分发的处理节点上。
- 保证所有子单元都能成功执行。
- 促进结果聚合。将结果返回到某个中心聚合节点上,或者放在某个可供稍后访问的地点。
保持对任何特定网格中间件的独立性
只要有可能,就应该避免在服务器端的应用程序中使用网格中间件。通常情况下,由网格中间件调用服务器端的应用程序就足够了。网格中间件负责处理安全、认证、资源的可访问性、环境变量等等问题。一旦这些条件都具备,应用程序只需要运行就可以了。
在这一小节剩下的内容中,我们将关注客户机,以及那些必须调用网格环境的服务器应用程序。
有若干种产品、工具箱,以及编程接口可供您构建与运行启用网格的应用程序。您在构建服务器应用程序时应该选择哪一种呢?
可根据运行小组选择的环境来选择部署和运行应用程序的产品。让应用程序尽可能不必感知中间件的存在。
对于客户机和服务器而言,根据其运行中间件的程度不同,您至少应该将使用特定中间件的部分分离出来,形成可被替换的组件。这些组件应该具有足够的隔离性,这样,当某个小组要将您的应用程序集成及部署到其目标环境中时,就会创建和替换这些组件。具备了这种模块性,部署组就不需要访问您的源代码。这些中间组件在您的代码和中间件之间构成了一个缓冲层,用模式的术语也可称为包装器外观(wrapper facade)。
假设有应用程序 A 和三个不同的网格中间件 M1、M2、M3。您应该将应用程序中所有知道网格中间件存在的代码都隔离到可以替换的接口库中,记为 L
n。这些接口库向应用程序 A 公开相同的接口。反过来,这些 L
n库也调用特定于某种中间件的 M
nAPI:
- A 调用 L。当使用网格中间件 M1 时,L1 将这些调用捕获过来,然后调用 M1。
- A 调用 L。当使用网格中间件 M2 时,L2 将这些调用捕获过来,然后调用 M2。
- A 调用 L。当使用网格中间件 M3 时,L3 将这些调用捕获过来,然后调用 M3。
L1、L2、L3 中公共可见的函数都具有相同的名称。然而,在每一个库内部,用于调用这些中间件的代码则依赖于特定中间件的应用编程接口。
应用程序 A 并不会改变。应用程序 A 并不知道某种特定的网格中间件的情况,也不知道使用的是哪一个 L。应用程序 A 不需要进行重新编译。集成与部署人员负责选择网格中间件 M。他们也负责创建适当的可替换库 L。对于某一个中间件接口来说,您应该提供一个经过测试的 L1,包括编译后的库和源代码,这样,部署人员就可以按照它创建 L2、L3 等等。
服务器
要实现策略 3:并行批处理,您应该考虑下面三个应用程序及用法方面的问题:可变的中粒度、互不干扰和独立性,以及软件许可。
可变的中粒度
对服务器程序进行调整时,您应该允许客户机指定每一次运行需要处理的工作项数目。这种
中粒度(midgrain)方法看起来并不像是最佳实践,而采用细粒度的方法,让服务器每次运行处理一个工作单元,似乎更像一些。但是,后者将导致效率低下:
- 客户机需要将工作分裂成独立的单元。
- 对于每一个单元,中间件都要完成优先级排序、调度和启动服务器程序的工作。
- 服务器程序需要对每一个单元都进行程序初始化以及相关的回收工作。
这些低效的因素并不是什么好事。
如果您允许客户机指定在每一个子单元中包含多少任务项,操作小组的人员就可以优化子单元的数量和大小。他们也可以根据作业的性质、网格中间件、网格中的系统,以及工作项大小的可变程度来调整。(假如每一个工作项包含着对某个帐号的处理,而在一个月当中某些帐号的活跃程度比其他帐号高。)比如说,在某个节点能力不平等的网格系统中,运行组可以使用中等可变的大小,并按照大小递减的顺序排序,这样就能保证在所有的工作都完成之前,差不多所有的节点都能够得到充分的利用。
互不干扰和独立性
策略 2 解决了不同用户发起的独立运行的作业之间相互冲突的问题。在策略 3 中,相同的问题又再次出现了,因为现在多个子作业是根据一个用户的请求并行运行的。现在,子作业之间必须不能产生冲突。
像前面一样,您必须避免在使用事务性资源的时候使用死锁模式。
像前面一样,您必须避免出现中间件热点,比如数据库热点。对共享记录的更新只能以偶尔进行,即便如此,也不能随意占用锁。
独立性特别适用于 Monto Carlo 仿真。在这种系统中,每一个子作业的随机数种子都必须截然不同,或是完全相同,这取决于应用程序的情况。“截然不同”的意思是可能需要预先计算好一些有 n 个数字(n = {2, 3, 4, 5, ...})的种子集合,这样,当工作被划分为 n 个子单元时,n 个种子就可以平均分布在生成序列中。大多数情况下随机数种子不是运行时参数,这时必须将应用程序改成能够接受一个随机数种子作为参数的形式。
软件许可
从软件许可的角度来看,允许并发的问题已经由策略 2 解决了,见前一篇文章所述(请参阅
参考资料)。然而,在并行批处理的模式下,许可控制软件可能需要处理新的情况。比如说,如果应用程序对每一个用户进行许可,而不是对机器-用户对,那么一个用户如果使用了三台机器,另一个人用了四台机器,那么需要的许可证数量将是两个,而不是七个。
结果聚合器
除非是聚合结果的工作简单到只需要将输出文件按照任意顺序连接在一起,否则您必须单独提供一个可以聚合结果的程序。请注意,将运行结果集中到一个地点上并不是聚合程序的任务。网格中间件只负责将运行结果集中在一起,或者允许远程访问这些结果。
如果处理结果内容所在的地方是收集程序可以直接访问的(如通过文件系统或 URL),那么,这个程序就不需要与网格中间件进行交互。将位置指定为聚合器的输入参数。如果聚合器必须运行网格中间件,那么程序员应该将与中间件相关的代码隔离开,可以采用上面
保持对任何特定网格中间件的独立性一节中使用的技术。
策略 3 总结
为了在服务器应用程序代码中启用网格,至少应该进行哪些工作?
可以考虑下面几点:
- 任务划分。客户机程序必须对工作进行划分,并将工作单元传递给网格中间件供其处理。
- 部分处理。服务器程序的每一个实例都必须能够处理工作中的一部分,同时由其他实例处理其他部分。
- 互不干扰与独立性。子作业必须是独立的,相互之间不会发生冲突。
- 聚合结果。必须有一个用于聚合所有子作业运行结果的程序或工具。
- 许可。不需要做什么工作。最坏情况下,当许可证是每用户方式时,需要验证某个应用程序是否占用了过多的许可证。
策略 4:服务
在策略 4:服务中,“服务”这个名词用于代替“Web 服务”(Web service)和“网格服务”(grid service);这两个名词在开放标准中具有特定的含义。策略 4 包括这两种类型的服务,但是也适用于在网格环境中实现其他面向服务的架构。
策略 4:服务提供下列特性:
- 客户机使用网格中间件调用服务。
- 根据客户机的行为,网格中间件将相应服务加载到内存中(如果有必要的话),然后调用该服务。
- 服务的结构是可调用的,通常是一个类、一个子例程库,或 DLL。
- 客户机和服务器之间是松耦合关系。
- 服务可由多个独立客户机共享。
- 服务可以在多次调用之间保持状态。
- 服务启动时间长不会存在问题。在服务长期运行的时候,每次运行过程中都会使用很多次,最好在第一次使用之前就将服务启动。
图 4: 客户机调用策略 4:服务中的一项服务
策略 4:服务是从启用网格的策略 2:独立并发批处理扩展而来,但除了并行的特性之外,策略 4 还具有策略 3:并行批处理的大多数性质。然而您也许还记得,应该在策略 3 和策略 4 之间选择一种实现,而不应该依次实现这两种策略。
对策略 4 而言,其基本例子是一个可接受命令行参数的程序,并根据命令行参数所指定的内容使用文件和数据库。这个程序已经具有了使用策略 1 和策略 2 启用网格应用所要求的属性,同时也具有策略 3 要求的大多数属性。用“客户机”和“服务器”这两个术语来讲,(请参阅
图 1),这个程序将成为可被远程客户机通过网格技术调用的服务器子例程。
用策略 4:服务启用网格应用带来的好处:
策略 4 的目标是允许服务器应用程序作为某种形式的远程过程调用的目标。客户机通过中间件调用服务。然后,如果有必要,中间件先将服务加载到内存中,然后再调用服务。
在这种策略中,网格的策略体现在其中的服务已经具有了前面几种网格启用策略所具备的特性,比如说可以根据网格中间件的选择,在其中很多台机器上运行等。需要服务的原因在于服务可以多次调用,而不需要反复进行批处理调度,程序初始化,以及稍后的终止回收工作。
举个例子来说,假设服务器应用程序必须花几秒钟进行软件许可检查,然后根据外部引用的数据在内存中构建几个 GB 的数据结构,接着处理输入信息。在策略 4 中,这个过程的花费不会超过一秒钟。由于有了服务,每一个服务实例都可以在一分钟之内处理多得多的独立请求。
服务器是策略 4:服务的焦点所在
客户机可能会通过利用您的服务和其他很多服务,构造出新的业务功能。策略 4 关注的是能够完成某种业务功能的服务程序,而不是通过网格中间件请求这些服务的客户机。为了达到最佳的功能划分,即便客户机应用程序和网格中间件是您自己的任务,您也可以将它们看作是其他人必须编写、实现和部署的工作。
然而,就像任何其他提供子例程库的人一样,您也必须提供样例程序,说明如何使用您的服务。
程序转变成服务
程序为转变成服务,必须具备一个或者多个可供外界调用的函数或子例程。同时必须将输入和输出信息定义为参数。
在 WSDL 中定义具有网格扩展功能的服务
为了和将来的标准保持一致,应该将函数定义在
Web 服务描述语言(Web Service Description Language,WSDL)中的
grid extensions段里。参数应该是
XML 数据类型,并且,在您具备足够多的经验之前,请选择
简单数据类型。
WSDL 中的
definable并不意味着您
已经为服务创建好了 WSDL,仅仅表示您已经
可以 这样做了。您应该避免对 WSDL 进行修改,也应该避免编写自己的方案。这样,当您将服务部署到 Web 服务和 OGSA 环境中时,就有足够的信心可以保证,客户机能够请求这项服务,网格中间件也能够调用它。
您应该为服务创建 WSDL,较好的做法是用某种工具根据服务的源代码生成 WSDL。稍后,负责开发、测试、部署、目录维护、客户端和服务端人员及相关工具就可以根据其各自不同的目的使用这个 WSDL。
可重用性
公共函数一旦加载,就应该(从实践上讲也必须)是可重用的。如果不可重用的话,网格中间件就需要在不同的使用之间卸载和加载程序,那么,对于每一次使用而言,该程序都必须进行初始化和终止回收的工作。服务至少应该是
串行可重用的。这种类型的可重用性意味着网格中间件可以按照客户机请求的顺序,一个接一个地将请求发送给您的函数。当然了,如果所使用的网格中间件要求函数实现可重用性甚至是线程安全性,您也必须满足这些要求。比方说,
Globus Toolkit 3 要求实现串行可重用性。
批量服务
当您创建服务的时候,应该既提供可处理一项信息的个体服务,也提供能处理一个请求中包含多项信息的批量服务。这样,您的客户机就可能将一次请求花费的代价分散到多个信息项上。
可重用服务优于独立执行的程序
环境 变量在服务初始化之前设置,而且在服务请求过程中不会改变。如果最初的程序实际依赖于从环境中获得每次请求中的参数,那么就必须将其转变成函数参数。在多次请求之间不会变化的环境变量应该保持为环境变量。
stdin、stdout 和
stderr 这三个流并不是服务用户从客户机接收输入,并发送回输出或错误信息的典型方法。用户程序如果用这些方式进行信息交换,那么就应该,通常也必须替换成其他方法,比如说字符串参数等。我们经常需要将一部分错误信息返回给客户机,而大部分错误信息则由服务或网格管理器保留下来,以供错误分析时使用。然而,诸如
Data Synapse 所提供的这类中间件的确提供了将 stdin 从客户机传送到服务,并将 stdout 和 stderr 从服务返回客户机的功能。
语言和运行环境规定的命名与行为规范
您的编程语言可能会规定主程序必须叫做 main,而子例程必须不能叫这个名字。如果有必要,您必须将 main 替换成其他的名字,如
main_function 。同时,您还必须提供其他一些所要求的函数,如下所示:
-
MS Windows 要求您的 DLL 中具有一个名为
DllMain 函数,以便在进程初始化和终止的时候,也就是第一个服务请求进来的时候,以及最后一个请求结束的时候使用。
- Globus Toolkit 3 (GT3) 要求您的程序具备
initialize 和
getOperations 这两个函数。
您可以将这些函数的标准形式粘贴到您的程序中,并根据您认为合适的方法将其增强。您可以编写独立于环境的函数,并从强制调用特定函数的环境中调用这些函数。比如说,在下面的代码中,
anyway_loaded 被 Windows 的
DllMain 和 GT3 中的
initialize 调用。
对平台和网格中间件的独立性
将代码直接放进程序中确实能获得针对特定环境的能力。这样平台和中间件对程序就不再是不可知的了。不过,我们确实可能针对某些环境编写一些额外的代码,而对另一些环境却没有。这种情况经常出现。
对某些环境提供函数的程序是多平台的,而不是独立于平台的。在非网格应用中,最典型的例子就是既是 applet 又是 application 的多线程客户端 Java 程序。
作为另一种可选的情况,您可能会想将与平台有关的代码隔离到单独的源代码模块中,供系统集成或部署人员修改。对特定于应用程序有关的这部分代码,您不需要提供源代码,但是可以提供一个例子,说明如何编写特定于平台的这部分代码。在 C 语言中,这些函数可构成一个独立的模块,然后连接到应用程序的目标代码中,从而构成一个 DLL 或其他形式的库。在 Java 中,那部分与框架有关的函数将作为超类提供给你,您可以从中继承自己的代码。在非网格环境中,这种情况的典型实例就是扩展
org.apache.struts.action.ActionServlet而非
javax.servlet.http.HttpServlet 的 servlet。
初始化与终止
不论是什么事情,只要能在服务初始化的时候做一次,就不要在每次服务请求到达时反复运行,应该在初始化时运行完成。这样的动作包括加载引用的内存结构、查找和连接其他系统,等等。稍后,当程序使用这些东西时,如果连接已经断开,则可以重新定位资源,重新连接。即便是稍后您的服务程序收到了操作系统或网格中间件发来的终止通知,它也应该将自己使用的资源释放掉。清单 1 提供了一个样例程序,您可将其作为模板使用:
清单 1. 完成多种目的的初始化和终止代码
// Treat this as pseudocode. C and Java code are intermixed
// C
int main_function (int argc, char *argv[ ])
{ anyway_loaded ( );
do_job_function (int argc, char *argv[ ]);
anyway_terminated ( );
}
void anyway_loaded ( ) { /* do one time initialization housekeeping */ }
do_job_function (int argc, char *argv[ ]) { /* do job's work */ }
/* recommended new function */
do_item_function (int argc, char *argv[ ]) { /* do one item's work */ }
/* recommended new function */
do_item_array_function (int argc, char *argv[ ]) { /* do several item's work */ }
void anyway_terminated ( ) { /* do one time termination housekeeping */ }
/* begin: for Windows DLL */
int DllMain (hModule, fwdreason, ipReserverd)
{/* Runs at process/thread attach/detach. Does DLL specific housekeeping */:
if (fwdreason==PROCESSATTACH)
{anyway_loaded ( )}
else
{anyway_terminated ( ); return completion_code; }
}
/* end: for Windows DLL */
/* begin: for GT3 OGSA
see http://www.casa-sotomayor.net/gt3-tutorial/multiplehtml/ch04.html
and http://wiki.nesc.ac.uk/read/pa9?UsingOGSA
*/
// Java
public void initialize(GridServiceBase base) throws GridServiceException
{ this.base = base; anyway_loaded ( ) }
public void preDestroy(GridContext context) throws GridServiceException
{ anyway_terminated ( ) }
/* end: for GT3 OGSA */
}
|
多个实例
正如在策略 2:独立并发批处理中那样,同一时刻您的服务例程可能有多个独立的实例在同一台或者多台不同的服务器上运行。
除非您的服务或服务的使用方式中有什么十分特别的地方,否则您一定要允许网格中间件调用服务的多个实例。启用网格策略 2 中的全部要求都适用于策略 4。
此外,如果您的服务在多次服务调用之间保持用户或其他状态信息,就必须完成下面的工作:
-
只有在诸如 OGSA 这样的环境中,才能保证来自同一个客户机的每一次服务请求都发送到同一实例上。
-
将状态信息持久保存在某处,以便其他实例就获取。每一个实例都必须知道,当前一次服务请求被其他实例处理时,该从何处获取状态信息。
考虑这样一个例子,三个节点中的每一个上都运行了您的服务的一个实例,从您的角度看就有三个实例。每一个客户机都会发出一系列相关的服务请求。如果服务是无状态的,或者可以从某个知识库中获得状态信息,同时有一个负载均衡器可以将进入的服务请求指定给任意一个节点,至于客户机,从它的视角所见到的就只有一个服务实例。原因在于服务只公开了一个地址,客户机也并不关心背后是否有多个实例,只关心可用性和吞吐量是否得到了提高。
多用户
根据网格中间件运行环境的不同,对允许多个客户机访问某个服务的一个实例的支持情况可能是不支持、可选项和强制支持三种。如果不支持多客户机访问,那么不论您做了什么或者没做什么,服务的每一个实例都只能由一个用户使用。如果是强制支持,那么您必须保证每一个程序实例都能处理多个用户。
如果程序要处理多用户的情况,那么它的安全处理代码就可能必须进行改变。服务可能需要清除或保存前一个用户的信息,哪怕前一个用户还可能提交另一次请求。环境中的安全控制机制能够确定某个客户机是否能够调用您的服务,但不能决定 User X 的客户机程序是否未经授权就用 User Y 的帐户号码提交服务请求。在必要的情况下,服务本身必须处理这种细粒度的授权。根据您的程序所使用资源的不同,程序可能必须在多个用户的访问之间修改其安全上下文(如客户机的用户 ID),而不应该使用通用的服务用户 ID,如 griduser4 之类。
一个实例支持多个用户带来的另一方面问题是并发性。如果网格中间件或程序环境可以保证一次只有一个服务请求到达每一个服务实例,那么串行可重用性就足够了。否则,您的程序就必须提供充足的线程安全性。就像很多其他的多用户服务环境,如 web 应用程序服务器与事务处理系统中那样,线程安全意味着即便是您的代码没有显式启动新的线程,也依然必须允许出现由运行环境启动多个线程的可能性。
软件许可
如果您的程序具有软件许可,那么您还可以选择使用新的许可模型,如对每次使用进行许可等。您可以决定一次使用中包含哪些内容,如一次服务请求,或由客户机发出的一组相关的服务请求等。
提供客户机与接口定义
您应该提供客户机程序调用服务器服务的示例程序。您的例子不应该只展示每个服务使用一次的情况,而应该体现出适当的使用序列、循环、以及批量使用服务的情况。集成或部署人员将调整您的客户机实例,使之适合所选的运行环境。对解决方案提供商而言,他们的客户机将使用您的服务,因此将用这个经过调整的客户机作为他们的起点。
客户机和服务器程序之间的轻耦合
客户机应该使用网格中间件,用安全的方式查找、启动以及连接服务器应用程序中的服务。也可以将每个服务请求通过中间件进行发送。然而,网格中间件意味着客户机和服务器程序之间存在轻量级或者粗粒度的耦合关系。典型情况下,从客户机经过网格中间件发送服务请求的频率应该低于每秒钟一次。
通过网格中间件进行服务调用与 Web 服务类似,成本相对于通过 socket 或命名管道编程接口按某种特定格式传送是比较高的。因此应该避免在客户机和服务器之间出现高容量的往复交互。如果不能避免,那么也应该通过一种相对(与 Web 服务相比)更有效的机制来传送,如基于 IIOP 的 CORBA 或 Java 远程过程调用。客户机应该使用网格中间件,用安全的方式查找、启动以及连接服务器应用程序中的服务,然后用其他的方式在前端和服务器之间进行必要的高速通信。
一直降低服务请求的网络传输数量的方法是将多个请求捆绑在一起,然后调用批量服务,处理同时到达的多个请求。这种情况在
转变成服务一节中讨论。
Tooling
在很多网格环境中,开发和部署过程中会对服务进行加工(tooling),主要涉及到直接从服务的源代码、WSDL、接口定义或头文件等创建客户机的存根程序以及其他一些元素。
比如说,
The Globus Toolkit 3 Programmer's Tutorial阐述了如何通过 tooling 创建灵活的服务操作提供者。这种技术与开放源代码的
SWIG tooling相结合,可以帮助我们根据 C 函数创建 GT3 OGSA 服务。
清单 2 显示,在 SWIG 的控制文件中没有任何与特定应用程序有关的代码来定义在生成的 Java 封装类中增加什么内容。IBM Server Allocation for WebSphere Application Server 的 Parallel Programming Environment 也提供了相似的机制。
清单 2. 普通的 SWIG 控制文件可使 C 语言子例程库适应于 GT3 OGSA 的要求
/*
See the resources section of this article for links to source material for this listing:
OGSA Service Provider tutorial
Wiki tips on using OGSA
GT3 reference material about OperationProvider and
GridServiceCallback
Simplified Wrapper and Interface Generator (SWIG)
*/
%pragma(java) jniclassimports=%{
import org.globus.ogsa.GridServiceBase;
import org.globus.ogsa.GridServiceCallback;
import org.globus.ogsa.GridServiceException;
import org.globus.ogsa.OperationProvider;
import org.globus.ogsa.GridContext;
import java.rmi.RemoteException;
import javax.xml.namespace.QName;
%}
%pragma(java) jniclassinterfaces="OperationProvider, GridServiceCallback"
%pragma(java) jniclasscode=%{
private static final QName[ ] operations = new QName[ ]{new QName("", "*")};
private GridServiceBase base = null;
private ServiceDataSet serviceDataSet = null;
public void initialize(GridServiceBase base) throws GridServiceException {
this.base = base; serviceDataSet = base.getServiceDataSet( ); }
public QName[ ] getOperations( ) {return operations;}
public void preCreate(GridServiceBase base) throws GridServiceException { }
public void postCreate(GridContext context) throws GridServiceException { }
public void activate(GridContext context) throws GridServiceException { }
public void deactivate(GridContext context) throws GridServiceException { }
public void preDestroy(GridContext context) throws GridServiceException { }
%}
|
策略 4:服务的总结
如果您要在服务器应用程序代码中增加启用网格服务的功能,至少应该完成哪些工作呢?
可以考虑以下几点:
- 将程序转换成服务。程序必须成为可由外部调用的子例程库,其中包含串行可重用的服务。
- 聚合处理。每一个服务都应该具备能处理一组项目的版本。
- 输入信息源。服务要求的细节信息必须转换成参数,而不是 stdin、stdout 或 stderr。
- 专用名称。程序必须按照约定使用或避免使用某些名称。
- 初始化与终止。程序应该将所有不需要在处理每一次服务请求时所做的事情都放在初始化或终止过程中完成。
- 多个实例。您应该假定每一个服务都具有多个实例。
- 多用户。您必须假定每一个服务可能有多个用户请求,每一个实例可能有多个用户请求,这些请求可能并发。
- 软件许可。不需要。您可以选择是否考虑实现一种基于“每次使用”的许可模型。
参考资料
关于作者  | 
|  | David Kra 是 IBM Grid Computing 组织的执行 IT 架构师。他在 IBM 度过的 27 年职业生涯中,一直在指导用户的分布式计算项目,从应用程序层到电缆连线,从需求到部署,他都可以提供建议。自从上世纪 70 年代以来,他提出了各种具有可扩展性、高容量、高可用性的通信解决方案,几乎涉及 IBM 的每一种平台,以及若干种非 IBM 的平台。他是 IBM Academy of Technology 的成员。 |
对本文的评价
|