对FLOW服务和大型文档进行映射化简的方法和服务器集群 【技术领域】
本发明涉及用于对例如电子数据交换(EDI)文档这样的大型文档的处理进行映射化简(MapReduce)的方法、服务器集群和计算机程序。
背景技术
企业环境中的现代软件应用通常被组织成多个子程序,其中每个子程序执行该软件应用的某些子任务。通常,例如在不同企业的应用之间的通信领域中,这种应用必须处理巨大量的数据,其中必须发送和处理大型文档。
这种应用通常是在集成服务器上执行的,集成服务器的一个示例是申请人的webMethods集成服务器。该集成服务器支持一种图形编程模型FLOW,FLOW用于定义集成服务器的处理逻辑。FLOW允许将多个FLOW服务图形化地定义为“黑盒”服务以及这些FLOW服务之间的管道,这些管道用于将数据从一个FLOW服务的输出传递到另一FLOW服务的输入。由于FLOW是图形编程语言,因此它减轻了开发者编写复杂且易出错的传统代码的负担。FLOW服务可用于处理任何种类的信息并用于执行各种计算。
现有技术已知的一种用集成服务器来处理大型文档的常见方法是以顺序方式处理文档的内容。然而,由于文档的大小可能在千兆字节的范围内,因此这种顺序处理是非常耗时且处理密集的,并且可能要求特殊的高端硬件,而这种硬件的维护是成本高昂且复杂的。
另一种现有技术已知的方法是采用代理(broker),该代理将大型文档分发到集成服务器的多个实例,以便实现某种并行处理。然而,该方法要求额外的并且经常是复杂的消耗传递中间件,以便在代理和集成服务器实例之间进行通信,这通常会造成较高的网络带宽要求并且导致对资源的高消耗。另外,该方法通常涉及由代理和集成服务器实例处理多个大型文档,其中每个大型文档仍由单个集成服务器以顺序方式来处理。
另外,在处理大型输入数据集的领域中,从Google公司的J.Dean等人所著的文献“MapReduce:Simplified Data Processing on Large Clusters”(OSDI′04:Sixth Symposium on Operating System Design and Implementation,San Francisco,December,2004)中知道了一种编程模型和相关联的框架,其被称为MapReduce(映射化简)。用户定义的映射(map)函数取得输入的对,并产生中间键/值对的一个集合。MapReduce库把与同一中间键相关联的所有中间值集合在一起,并把它们传递到用户定义的化简(reduce)函数。化简函数接受一中间键和一个值集合。它将这些值合并在一起以形成一个可能较小的值集合。通常每次化简调用将产生零个或一个输出值。中间值经由迭代器被提供到用户的化简函数。这样就允许了处理太大以至于不能装入存储器中的值列表。以这种编程模型编写的程序可以自动被该框架在不同机器上并行执行。然而,将MapReduce编程模型用于现有应用上要求对该应用的编程逻辑进行深入的适应性修改以便符合MapReduce编程模型。另外,MapReduce是意图用于搜索引擎领域的,在这个领域中,诸如对大批文档中的单词计数、构建web链接的图结构等等之类的专门任务是常见的。
大型文档处理的一个具体示例是电子数据交换(EDI)。EDI涉及通过电子手段在应用之间传输结构化的消息。EDI通常用于在不同企业的应用之间传输诸如发票或购买定单之类的大型文档。结构化消息的若干种标准化结构是现有技术中已知的,例如ANSI X12、UCS、VICS、UN/EDIFACT、ODETTE和EANCOM。对这种大型EDI文档的处理通常涉及上述的缺点。
因此,本发明所基于地技术问题一方面是提供一种方法和系统,用于以较少的处理时间和计算工作来处理大型文档尤其是EDI文档,从而至少部分地克服以上说明的现有技术的缺点。本发明所基于的另一个相关的技术问题是提供一种方法和系统,用于以较少的处理时间和计算工作来处理FLOW服务的输入,并且其只需要最低限度的适当性修改工作就可灵活地适应于现有的编程逻辑。
【发明内容】
根据一个方面,本发明涉及一种用于对电子数据交换(EDI)文档的处理进行映射化简的方法。在权利要求1的实施例中,该方法包括以下步骤:
a.将EDI文档映射到多个中间文档;
b.处理中间文档以产生多个中间结果;
c.化简多个中间结果以产生多个经化简的中间结果;以及
d.化简经化简的中间结果以产生表示经处理的EDI文档的最终结果。
本发明的第一方面是基于以下认识的,即MapReduce的概念不仅可用在搜索引擎的上下文中,而且也可有利地用于企业环境中的EDI文档的处理。因此,大型EDI文档首先被映射即分割为多个中间文档。映射即分割优选是这样执行的:使得每个所得到的中间文档具有大致相等大小的有效载荷,即,使得当其在该方法的其他步骤中处理时消耗同等量的处理时间和/或处理资源。
中间文档随后被处理以产生多个中间结果,这优选是并行执行的,以便改善就整体处理时间而言的处理性能。另外,由于EDI文档被映射到多个通常更小的中间文档,因此中间文档可通过普通硬件来处理,即不需要采用专门的高端硬件。
在对中间文档进行处理之后,所得到的中间结果被化简以产生多个经化简的中间结果。化简意味着将相关的中间结果整合为一个经化简的中间结果。此上下文中的“相关”指的是两个或更多个中间结果源自同一原始EDI文档。
最后,经化简的中间结果在另一个步骤中被化简以产生最终结果。该方法步骤通常涉及适当地组合经化简的中间结果,以便获得EDI文档处理的最终结果。
如果化简步骤是在不同物理机器上执行的以便实现并行化,则上述的两步化简尤其有利。在此情况下,由于中间结果在被发送到执行第二化简步骤的另一机器之前已被化简,因此宝贵的网络带宽可得以节省,因为在机器之间需要传输的结果更少。另外,如果化简步骤是满足交换律(即,A operation B相当于B operation A)和/或满足结合律(即,A operation(Boperation C)相当于(A operation B)operation C)的操作,则这个方面尤其有利。因此,化简步骤可以按任何顺序并行执行。与两步化简相关联的另一个优点在于负担即用于执行化简步骤的处理时间可被分摊在多个普通机器上,而不是一个机器在一个大集合上进行化简步骤。第二化简步骤从而可在较小的中间结果集合上执行。
在本发明的另一个方面中,EDI文档(1)可以按下述方式来映射:使得中间文档(10,11)中的每一个包括EDI文档(1)的多个交换包封中的至少一个、多个功能组包封中的至少一个和/或多个事务集包封中的至少一个。因此,映射即分割可在由EDI文档的结构定义的边界之一处执行,即在事务集包封级上执行、在功能组包封级上执行和/或在交换包封级上执行。它通常依赖于最终用户用来基于EDI文档的结构定义在哪个边界上分割EDI文档。例如,如果功能组和/或交换包封包含最优数目的事务来定义合理大小的有效载荷。
步骤a.和d.,即映射和最终化简,可由服务器集群的主服务器执行,并且步骤b.和c.,即分别处理和化简中间文档或中间结果,可由该服务器集群的多个从属服务器执行。每个从属服务器可处理一个或多个中间文档并且化简一个或多个中间结果。通过多个从属服务器来执行处理是尤其有利的,因为中间文档的处理能够被高度并行化。主服务器和从属服务器优选是通过网络连接通信的不同物理机器。
例如,如果处理任务是将一列数字{1,2,3,4,5,6,7,8,9,10,11,12}加起来,则主服务器可将中间文档{1,2}委托给从属节点1,将{3,4}委托给从属节点2,将{5,6}委托给从属节点3,将{7,8}委托给从属节点1,将{9,10}委托给从属节点2,并且将{11,12}委托给从属节点3。在从属节点1处,中间结果于是将是中间文档的加和:对应于{1,2}的3,以及对应于{7,8}的15。从属节点1上的化简步骤于是将把3和15相加,得到18。相应地,从属节点2上的化简步骤将把7和19相加以得出26,并且从属节点3将把11和23相加以得到34。因此,只有三个经化简的中间结果18、26、34需要被传送回主服务器,而不是传送3、15、7、19、11、13。在主服务器上执行的最终化简步骤于是将得出78(18+26+34),这是期望的结果。
在另一个方面中,该方法还包括以下步骤:通过异步调用将中间文档从主服务器发送到从属服务器。因此,主服务器取得大型EDI文档并将中间文档的处理委托给从属服务器。EDI文档本身优选地与主服务器保持在一起。异步调用意味着一旦主服务器调用即触发了从属服务器的处理(这优选由主服务器的线程池来执行),主服务器线程并不等待从属服务器完成其处理(这将是同步调用),而是主服务器可以立即继续进行其自己的处理,即随后调用其他从属服务器。此概念进一步增大了本发明的处理速度,因为不存在被阻塞的主服务器资源(即,等待从属服务器),从而使得能够将任务更快地委托给从属服务器。
或者,EDI文档可被存储在从属服务器可访问的分布式文件系统中,并且该方法还可包括以下步骤:主服务器通过异步调用把对中间文档(10,11)的引用标记(reference)发送到从属服务器。如果从属服务器通过直接连接与该分布式文件系统相连接,则这个方面可以大大加快处理,因为不必通过缓慢的网络连接来发送EDI文档或中间文档。在此情况下,只有对EDI文档和/或EDI文档的应当被从属服务器处理的部分(即,中间文档)的引用标记需要被传递到从属节点。协同定位(co-location)(即,提供对实际存在于从属节点本身上的部分的引用标记)尤其有利,因为它节省了许多带宽消耗,因为在机器之间不发生EDI数据传送。
当处理中间文档时,从属服务器优选地在本地将中间结果存储在存储器或持续性文件系统中,这些中间结果随后被主服务器所收集。
另外,中间结果中的每一个可包括把相应中间结果与EDI文档联系起来的标识符。通过使用标识符,这些中间调用结果中的每一个可被追踪到原始调用。标识符可以是例如随着利用大型EDI文档进行的每次原始调用而增大的计数器。标识符可用于允许在主服务器调用从属服务器时的异步行为。这个方面可以释放主服务器处的委托线程(在同步模式中,这些线程必须等待从属服务器执行其处理),从而使得主服务器处的资源利用更好并且间接地带来了更大的并行化。当主服务器以异步方式将中间结果委托给从属服务器时,从属服务器从而拥有了将其获得的中间结果追踪回来自主服务器的原始调用的手段。例如,如果存在要处理的大型EDI文档,则可为该调用创建标识符“12345”。该方法可在将中间文档委托给从属服务器的同时将该标识符传递到从属服务器。这帮助了在后续化简步骤中将所有中间结果与原始EDI文档联系起来,因为在从属服务器处可利用此标识符来维护中间结果。
作为附加或替换,用于执行步骤b.中从属服务器的处理的处理逻辑可在运行时期间被分发到从属服务器。因此,从属服务器不必拥有执行EDI文档的可执行文件即处理逻辑的拷贝。这些可执行文件例如可被包括在主服务器的库中并在运行时被散布到从属服务器。散布优选地是依据当前EDI文档所需的可执行文件来执行的。任何对等框架或专属机制都可用于共享可执行文件。
另外,本发明涉及一种服务器集群,其包括适合用于执行以上给出的方法中的任何一种的主服务器和多个从属服务器。
在本发明的另一个方面中,提供了一种用于对FLOW服务的至少一个输入的处理进行映射化简的方法。在权利要求9的实施例中,该方法包括以下步骤:
a.通过映射器服务将FLOW服务的至少一个输入映射到多个中间输入;
b.执行FLOW服务的多个实例(F10,F10’),FLOW服务的实例处理中间输入以产生多个中间结果;
c.通过多个第一化简器服务将中间结果化简为多个经化简的中间结果;以及
d.通过第二化简器服务来化简经化简的中间结果以从经化简的中间结果产生FLOW服务的最终输出。
因此,FLOW服务(不论是新创建的还是现有的FLOW服务),并不以顺序方式来处理其输入,而是FLOW服务的处理通过上述方法被有效地“并行化”。因此,FLOW服务的输入不像通常执行的那样被直接馈送到FLOW服务中,而是首先被映射器服务分割成多个中间输入。在一个实施例中,FLOW服务本身被“克隆”,即中间输入被FLOW服务的多个实例所处理,优选是并行地处理。所得到的中间结果随后被多个第一化简器服务所化简,以便对于FLOW服务的每个实例获得一个经化简的中间结果。最后,经化简的中间结果被第二化简器服务所化简,以便提供FLOW服务的最终输出。优选地,第二化简器服务是基于与多个第一化简器服务相同的实现的,即,所有化简步骤都是由同一化简器服务的多个实例执行的。在下文中,为清楚起见,所使用的术语“化简器服务”和“化简器服务的实例”是同义的。应当注意,FLOW服务的整体输入和输出保持相同,只不过处理被并行化了。
在一个方面中,映射器服务和第二化简器服务是在服务器集群的主服务器上执行的,并且FLOW服务的多个实例和多个第一化简器服务是在所述服务器集群的多个从属服务器上执行的。
在另一个方面中,映射器服务的输入签名符合FLOW服务的输入签名。作为附加或替换,化简器服务的输出签名符合FLOW服务的输出签名。输入签名(或输出签名)优选地定义作为服务的输入(或输出)提供的变量的数目和类型,因此定义了服务的接口。
由于映射器服务的输入签名优选地符合要并行化的FLOW服务的输入签名这一事实,因此任何现有的FLOW服务都可被连接到具有相同输入签名的映射器服务。另外,任何现有的FLOW服务都可被连接到具有相符的输出签名的化简器服务,这意味着任何现有的FLOW服务都可被嵌入在本方法中,而无需对其输入或输出签名或内部处理逻辑进行适应性修改。这是尤其有利的,因为它大大增加了本发明的灵活性和可应用性。输入和输出签名的示例在以下详细描述中给出。
在另一个方面中,FLOW服务的至少一个输入可包括电子数据交换(EDI)文档。因此,FLOW服务在这个方面中优选适合用于处理EDI文档。当FLOW服务被并行化时,可以实现EDI文档的尤其高效的处理,这类似于以上进一步给出的那些方面。然而,应当明白,FLOW服务完全不限于处理EDI文档。相反,FLOW服务适合于处理任何种类的文档,例如XML文档。另外,不仅可通过FLOW服务处理文档,还可实现任何种类的计算逻辑。
本发明还涉及一种服务器集群,其包括适合用于执行以上给出的方法中的任何一种的主服务器和多个从属服务器。
最后,提供了一种计算机程序,其包括适合用于实现上述方法中的任何一种的指令。
【附图说明】
在以下详细描述中,参考以下附图进一步描述本发明的当前优选的实施例:
图1:本发明的实施例的示意性概览;
图2:根据本发明实施例的主服务器和多个从属服务器的示意性概览;
图3:EDI文档的结构概览;
图4:示例性FLOW服务及其相关输入和输出;
图5:另一个用于处理EDI文档的示例性FLOW服务;
图6:用于指定被映射化简的FLOW服务的属性的图形用户界面的概览;以及
图7:本发明的实施例的示例性实现方式的类图。
【具体实施方式】
在下文中,针对服务器集群根据本发明对大型EDI文档的处理来描述本发明的当前优选的实施例。如图2中示意性示出的,也被称为网格的服务器集群是一种分布式的计算平台,其允许并行处理。它通常包括联网的、松散耦合的计算机的集群,这些计算机协同动作以执行极大的计算或数据密集型任务。应当明白,处理EDI文档仅是本发明的许多种场景中的一种,任何其他类型的文档也可被处理。另外,本发明不仅可以有利地实现文档处理,还可以有利地实现任何种类的复杂计算,以下的更多示例性实施例将证实这一点。
EDI文档的一般结构在图3中示意性地示出,图3示出了例如由ANSIASC X12标准定义的结构。因此,EDI文档包括任意数目的事务,这些事务被按各种包封(envelop)来分组。在最内部的级别上,事务集由图3所示的ST/SE片段(segment)来标识。ST片段优选地包括事务集ID、控制号码和可选的实现规约参考标记。SE片段优选地包括该事务集中包括的片段的数目以及与ST片段相同的控制号码。第二包封级别是功能组包封。其目的是对一次传输内的相似类型的事务集分组。ANSI ASC X12定义了若干个用于对类似的事务集分组的业务过程,比如规划调度(830)、购买定单(850)、购买定单确认(855)、购买定单改变(865)、定单状态查询(869)或定单状态报告(870)。
最外部的级别是由ISA和IEA片段定义的交换包封(参见图3)。交换包封优选地包含了从一个发送者到一个接收者的数据。ISA片段优选为固定长度的片段。ISA/IEA片段中包含的一些项目是发送者和接收者的结构化的邮箱地址、交换控制号码、交换包封内的功能组计数、时间/日期戳以及交换包封的版本。
处理这种EDI文档的通常方式可能是将EDI文档的数据映射到另外的格式(例如,后端系统所要求的格式),或者将来自EDI文档的数据映射到FLOW服务的输入,下文将进一步概述这一点。
传统的EDI处理通常一次处理一个事务。如果EDI文档大小大约为数百兆字节或千兆字节,则此处理是非常耗时的。为了在某种程度上减轻这一缺点,通常部署高端服务器的集群来并行地处理多个EDI文档中的每一个。然而,对高端服务器的采用具有严重的缺点,例如,如果在处理期间硬件/软件发生故障则复杂度会增大,以及所有者为了维护高端服务器而成本增加。
本发明定义了一种用于在EDI文档级上并行化处理的方法和服务器集群。从图1可以看出,主服务器M1首先接收大型EDI文档1。该EDI文档首先在交换包封边界上被映射即分割成多个中间文档10、11。然而,应当明白,取决于EDI文档的类型,在本发明的其他实施例中EDI文档也可在例如功能组包封级或者甚至在事务集包封级被分割。
甚至更细粒度的方法也适合于本发明,例如在单事务级分割EDI文档,如果EDI文档中的事务是独立的实体的话。结果,文档可被映射(分块)为非常小的部分,从而带来了高水平的并行化。
在分割EDI文档之后,主服务器M1将中间文档10、11委托给多个从属服务器S1、S2处理。从属服务器S1、S2处理中间文档10、11并且产生中间结果20-23。应当注意,对一个中间文档的每次处理可能产生多个中间结果,下文将进一步说明这一点。
中间结果20-23随后被从属服务器S1、S2中的每一个化简,以便优选地在每个从属服务器S1、S2中获得一个经化简的中间结果30、31。
当主服务器M1完成了将中间文档委托给从属服务器S1、S2时,它优选地在从属服务器S1、S2中的每一个上发出化简调用。委托优选地是以异步方式调用的,从而使得主服务器M1(即,其线程)可以继续进行其处理而不必等待每个从属服务器S1、S2完成执行,这一点已在上文中说明了。
主服务器M1发出的化简调用引起从属服务器S1、S2将其各自化简的中间结果30、31发送回主服务器M1。主服务器M1随后发出另一化简调用以便将收集到的经化简的中间结果30、31整合为一个最终输出2。输出2于是代表了经处理的EDI文档1。
应当注意,由于从属服务器S1、S2中的每一个仅处理整个EDI文档1的一部分,因此不需要专门的高端硬件。普通设备可用作从属服务器,这大大降低了整个体系结构的成本。
主服务器和从属服务器的处理优选的由若干个服务来执行。尤其优选的是其中服务器是webMethods集成服务器的实施例。webMethods集成服务器处于申请人的webMethods系列产品的核心处。它是基于Java的多平台企业集成引擎,该引擎支持服务的执行,以执行诸如数据映射和与其他系统的通信之类的集成逻辑。该集成服务器提供了一种图形编程模型FLOW,FLOW用于执行诸如映射、调用其他服务、循环和分支之类的常见集成任务。该集成服务器的一些特征包括编写图形FLOW和java服务,定义和修改文档和映射逻辑,测试、调试和执行服务,创建和配置web服务,以及编辑适配器服务和通知。
图4示出了示例性的简单FLOW服务“sampleFlowService”,其取得两个整数“input1”和“input2”,并且提供两个输出“multiplyIntsResult”(将两个输入整数相乘的结果)和“addIntsResult”(将两个输入整数相加的结果)。当在集成服务器上执行该示例性FLOW服务时,可向用户提供用于输入这些输入的值的对话,并且可呈现包括计算结果的另一对话。图4示出了被开发者优选用于指定输入、FLOW服务和输出之间的映射的图形用户界面。
FLOW服务处理的另一示例是对文件中单词的出现计数。常见的没有并行化的方法是逐行地读取该文件,将单词作为键添加在HashMap中并将计数作为值添加在HashMap中。首先,就该键来查询HashMap,并且如果查询返回“null”(空),则计数被置为1。否则,原始计数被取得,并且它将被递增并被放回HashMap中。当映射器M10和化简器服务R10、R11、R20被编写时,映射器服务可以产生较小的文件作为输出,并且化简器服务仅将输出HashMap与最终HashMap相组合。因此,进行单词计数的原始FLOW服务的输入/输出签名保持相同,而只有映射器和化简器操作的逻辑需要被编写。这是本发明的一个尤为有利的方面,下文将进一步说明这一点。
FLOW服务的另一个示例是EDI文档的处理。图5示出了示例性的FLOW服务“mainService”,其取得EDI文件名作为输入。它通过调用服务“ediToValues”(也在图5中示出)来将EDI文件格式转换为内部webMethods格式。作为输出,它返回所输入的EDI文件在转换后是否整体上是有效的。它还可指示出执行服务所消耗的处理时间(图5中没有示出)。FLOW服务“ediToValues”的输入/输出签名是如下组织的:它接受输入“ediFileName”(EDI文档的文件名)或输入“edidata”(被表示为串的实际EDI数据本身),这两者是互斥的。如果“printTime”输入被设置,则执行服务所花的时间将被打印出来。单个输出“isValid”将被输出,以表明EDI文档在转换后是否有效。
由于对上述FLOW服务“ediToValues”的处理顺序地消耗了大量处理时间和资源,因此以下论证本发明的方法如何被应用到这种现有的FLOW服务上以便高效并且灵活地对其进行“并行化”。
参考图6,在FLOW服务“ediToValues”的属性面板中,开发者可以提供以下属性:
·映射器服务:用于映射输入数据的有效集成服务器服务
·化简器服务:用于化简输出数据的有效集成服务器服务
·网格使能:设置为“真”
·扼流(Throttle):包括主服务器和从属服务器在内的并行执行的最大数目
·策略:此属性指明是将从属服务器的中间结果保存在存储器中(如果它们的大小可忽略的话)还是将它们持续保留在文件中。
然后本发明使用上述的映射器服务M10(参见图1)来执行将EDI文档1映射到多个中间文档10、11。映射器服务M10的输入和输出签名优选地遵循某些规则:
·映射器服务的输入优选地与被“并行化”的FLOW服务(在该示例中是“ediToValues”)相同。在此情况下,映射器服务接受与服务“ediToValues”的输入相匹配的输入“ediFileName”。
·映射器服务的输出优选地被包裹在名为“serviceInputData”的集成服务器文档中。“serviceInputData”的内容优选地是被“并行化”的FLOW服务的输入。在该示例中,映射器服务的输出“edidata”与服务“ediToValues”的输入相匹配。
·另外,映射器服务的输出优选地提供布尔型的“isLastSplit”。映射器服务在处理最后的映射步骤时将该值设置为“真”。映射器服务于是可反复地被调用,直到该值被设置为“真”为止。
化简器服务R10、R11、R20的输入和输出签名优选地也遵循某些规则:
·化简器服务的输入被包裹在称为“reduceInputData”的集成服务器文档列表中。该文档列表优选地是文档阵列。该文档列表中的每个条目的内容可以是要被“并行化”的FLOW服务的输出。在该示例中,化简器服务的输入“isValid”与服务“ediToValues”的输出相匹配。
·化简器服务的输入还可提供布尔型的“isLastReduceStep”。如果化简器处理最后的化简调用,则该值可被设置为真。这可用于在化简器服务中执行清理活动。
·化简器服务的输出应当是要被“并行化”的服务的输出。在该示例中,输出“isValid”与服务“ediToValues”的输出相匹配。
可以看出,以上定义的映射器和化简器服务符合FLOW服务的输入和输出签名。这具有这样的优点,即任何现有的FLOW服务都可以很容易地被“并行化”,因为FLOW服务本身的签名和内部处理都不必被适应性修改。映射器和化简器服务只是简单地被分别“插入”在FLOW服务之前和之后。
如果化简操作是满足结合律和交换律的,则以上给出的方法可被尤为有利地应用。例如,当计算1至100的范围中的质数的量时,可以采用两个输入分割;第一输入分割为1至50,第二输入分割为51至100。此示例中的中间输出将为分别表示这两个分割中的质数数目的“x”和“y”。化简操作将进行加法,该加法是可结合且可交换的。
以上给出的签名符合性是本发明比现有技术已知的传统MapReduce算法有利的优点之一。用户编写的传统MapReduce的映射步骤取得输入的对并产生中间键/值对的集合,而根据本发明的集成服务器上的映射器服务遵循标准的签名并且只是对输入数据进行“分块”,即分割。另外,传统的MapReduce的映射步骤通常是在取得输入对并产生中间键/值对的集合的从属服务器上运行的,其中集成服务器上的映射器服务优选地在主服务器M1上执行,主服务器M1随后将分块的输入数据委托给从属服务器S 1、S2以便执行实际服务。这一点尤其灵活并且使得flow服务易于开发和维护,其若干原因如下:在传统的MapReduce算法中,不存在被“并行化”的服务,而其更确切地说是通过执行期望操作的映射器和化简器来定义的编程构造。与集成服务器中不同,不存在与期望操作相对应的服务。这使得所要求保护的方法易于理解并且尤其对用户友好。
至于由用户编写的传统MapReduce的化简步骤,它取得中间键并且该键的值集合,并且将这些值合并以形成可能较小的值集合。相反,当化简器服务在根据本发明的集成服务器上执行时,主服务器M1优选地向所有从属服务器S1、S2发出化简调用,以整合相关的中间结果。当从属服务器S1、S2中的每一个中的化简操作之后主服务器M1从从属服务器S1、S2取回结果时,主服务器M1在内部将这些结果组合成一个最终结果。这实质上使得化简操作成为一个两步过程,该过程首先在从属服务器S1、S2上执行,然后在主服务器M1上执行,这节省了网络带宽,从而带来了处理时间的进一步减少以及对资源的更好利用,以上已对这一点进行了说明。
本发明的服务器集群的其他特征也是可能的。主集成服务器例如可维护一配置文件,该配置文件包括可用的从属服务器的列表。它可包括主服务器将处理委托给从属服务器所需要的信息。这个简单的工具很容易被扩展来实现从属节点的动态识别。例如,当从属服务器启动时,它可以向服务器集群中的所有机器广播其标识,并且主服务器可以将该从属服务器识别为潜在的从属服务器。
在下文中,给出本发明的实施例的示例性Java实现,其主要组件在图7中示出。然而,应当明白,本发明既不限于编程语言Java,也不限于下文中示出的具体实现。
图7所示的类JobClient用于定义“作业”(job),该作业代表根据本发明对数据处理的一次执行。JobClient的示例性实现在以下代码列表中示出:
package com.wm.app.b2b.server.mapred;
import com.wm.data.IData;
import com.wm.lang.ns.NSName;
import com.wm.util.UUID;
public class JobClient
{
private JobConfiguration jobConf=null;
private JobInProgress jobInProgress=null;
protected NSName mapper=null;
protected NSName reducer=null;
protected NSName mapTaskName=null;
protected int throttle=0;
protected boolean ispersistMapIntResult=false;
protected boolean isStoreMapIntResult=false;
protected String jobId=null;
protected ClusterHosts clusterHosts=null;
protected HostSelector hostSelector=null;
public JobClient(NSName mapper,NSName reducer,NSName mapTaskName,
int throttle,boolean isPersistMapIntResult)
{
this.mapper=mapper;
this.reducer=reducer;
this.mapTaskName=mapTaskName;
this.throttle=throttle;
this.isPersistMapIntResult=isPersistMapIntResult;
jobConf=new JobConfiguration();
clusterHosts=new ClusterHosts();
hostSelector=new RoundRobinHostSelector();
hostSelector.setClusterHosts(clusterHosts);
jobConf.readJobConfiguration(clusterHosts);
jobId=UUID.generate();//比整数好,因为在服务器重启前后它将是唯一的
}
public JobClient(NSName mapper,NSName reducer,NSName mapTaskName,
int throttle,boolean isPersistMapIntResult,boolean isStoreMapIntResult)
{
this(mapper,reducer,mapTaskName,throttle,
isPersistMapIntResult);
this.isStoreMapIntResult=isStoreMapIntResult;
}
public void submitJob(IData pipeline)
{
jobInProgress=new JobInProgress(this,pipeline);
jobInProgress.executeAndTrackJob();
}
}
可以看出,当JobClient的新的实例通过调用其构造器而被创建时(参见第15页第17行),它以参数mapper(要用于当前作业的映射器实现)、reducer(要使用的化简器实现)、throttle(期望的并行服务执行的数目)以及isPersistMapIntResult(中间结果是应当被存储在从属服务器的存储器上还是存储在持续性的文件系统中)作为输入。当调用submitJob()方法时(参见第16页第4行),该方法取得类型为IData的pipeline参数,该参数优选地包括要处理的输入数据,例如EDI文档的数据。submitJob()随后创建新的JobInProgress实例并且调用其executeAndTrackJob()方法。
JobInProgress的示例性实现在以下代码列表中示出:
package com.wm.app.b2b.server.mapred;
import java.util.ArrayList;
import com.wm.app.b2b.server.InvokeState;
import com.wm.app.b2b.server.Service;
import com.wm.app.b2b.server.Session;
import com.wm.app.b2b.server.ThreadManager;
import com.wm.data.IData;
import com.wm.data.IDataCursor;
import com.wm.data.IDataUtil;
public class JobInProgress implements Runnable
{
private TaskInProgress taskInProgress=null;
private int mapTaskID=0;
private int reduceTaskID=0;
protected JobClient jobClient=null;
protected TaskTracker taskTracker=new TaskTracker();
private InvokeState invokeState=null;
private Session session=null;
private boolean isJobDone=false;
IData mapperPipeline=null;
public JobInProgress(JobClient jobClient,IData mapperPipeline)
{
this.jobClient=jobClient;
this.mapperPipeline=mapperpipeline;
}
public void executeAndTrackJob()
{
if(InvokeState.getCurrentState()!=null){
invokeState=
(InvokeState)InvokeState.getCurrentState().clone();
}
if(InvokeState.getCurrentSession()!=null){
session=
(Session)InvokeState.getCurrentSession().clone();
}
//long startTime=System.currentTimeMillis();
ThreadManager.runTarget(this);
synchronized(this){
while(isJobDone==false){
try{
this.wait();
}catch(InterruptedException e){
}
}
}
//long endTime=System.currentTimeMillis();
//System.out.println(″The time taken in mill secs:″+(endTime
-startTime));
}
public void run()
{
boolean isAllMapTasksDispatched=false;
ArrayList<Integer>mapTaskIdsCompleteList=null;
Integer[]mapTaskIdsComplete=null;
IData reducerPipeline=mapperPipeline;
IDataCursor reducerPipelineCur=null;
InvokeState.setCurrentState(invokeState);
InvokeState.setCurrentSession(session);
taskInProgress=new TaskInProgress();
int numMapTasksComplete=0;
while(true)
{
//检查是否分派了所有映射任务
if(!isAllMapTasksDispatched){
//分割器
mapTaskID++;
IDataCursor mapperPipelineCursor=
mapperPipeline.getCursor();
IDataUtil.put(mapperPipelineCursor,
MapReduceConstants.MAP_ITERATION,mapTaskID);
try {
Service.doInvoke(jobClient.mapper,
mapperPipeline);
}catch(Exception e){
e.printStackTrace();
break;
}
//端点服务
IData serviceInputData=
(IData)IDataUtil.get(mapperPipelineCursor,
MapReduceConstants.SERVICE_INPUT_DATA);
IDataUtil.remove(mapperPipelineCursor,
MapReduceConstants.SERVICE_INPUT_DATA);
isAllMapTasksDispatched=
IDataUtil.get(mapperPipelineCursor,MapReduceConstants.IS_LAST_SPLIT)==
null?false:true;
if(isAllMapTasksDispatched){
IDataUtil.remove(mapperPipelineCursor,
MapReduceConstants.IS_LAST_SPLIT);
IDataUtil.remove(mapperPipelineCursor,
MapReduceConstants.MAP_ITERATION);
}
mapperPipelineCursor.destroy();
//System.out.println(″spawning map task:″+
mapTaskID);
MapTask mapTask=TaskFactory.createMapTask(this,
mapTaskID);
mapTask.setTaskInput(serviceInputData);
mapTask.setHostSelector(jobClient.hostSelector);
//扼制并行执行的mapTask的最大数目
//它考虑了先前提交和完成的映射任务
synchronized(taskTracker){
try{
numMapTasksComplete=
taskTracker.getNumCompletedMapTasks();
while((mapTaskID-numMapTasksComplete)
>jobClient.throttle){
//System.out.println (″waiting for
some map tasks to complete,num mapTasks onGoing:″+(mapTaskID-
numMapTasksComplete));
taskTracker.wait();
numMapTasksComplete=
taskTracker.getNumCompletedMapTasks();
}
}catch(InterruptedException e){
//待做事项
}
}
taskInProgress.executeTask(mapTask,invokeState,
session);
}else if(jobClient.reducer!=null&&
isAllMapTasksDispatched) {
//化简器
synchronized(taskTracker){
mapTaskIdsCompleteList=
taskTracker.getCompletedMapTasks();
while(mapTaskIdsCompleteList==null ||
mapTaskIdsCompleteList.size()==0){
try {
taskTracker.wait();
}catch(InterruptedException e){
}
mapTaskIdsCompleteList=
taskTracker.getCompletedMapTasks();
}
}
if(mapTaskIdsCompleteList!=null&&
mapTaskIdsCompleteList.size()>0){
mapTaskIdsComplete=
mapTaskIdsCompleteList.toArray(new Integer[0]);
reduceTaskID+=mapTaskIdsComplete.length;
//System.out.println (″processing reduce
tasks:″+mapTaskIdsComplete.length);
if(mapTaskID==reduceTaskID){
reducerPipelineCur=
reducerPipeline.getCursor();
IDataUtil.put(reducerPipelineCur,
MapReduceConstants.IS_LAST_REDUCE_STEP,true);
reducerPipelineCur.destroy();
}
ReduceTask reduceTask=
TaskFactory.createReduceTask(this,reduceTaskID);
reduceTask.setTaskInput(reducerPipeline);
reduceTask.setCompletedMapTasks(mapTaskIdsComplete);
reduceTask.runTask();
//如果由于某种原因,化简任务之一返回了null,
//此结果不应当造成以前收集的所有其他化简任务输出无效
//待做事项:当整个框架完成时,失败的(返回了null的)
//化简任务应当被重新执行
IData tempReduceOutput=
reduceTask.getTaskResult();
if(tempReduceOutput!=null){
reducerPipeline=tempReduceOutput;
}
}
//所有映射和化简任务完成
if(isAllMapTasksDispatched && mapTaskID==
reduceTaskID){
IDataCursor reduceServicePipelineCur=
reducerPipeline.getCursor();
IData reduceIntermediateOutput=
(IData)IDataUtil.get(reduceServicePipelineCur,
MapReduceConstants.SERVICE_OUTPUT_DATA);
IDataUtil.remove(reduceServicePipelineCur,
MapReduceConstants.SERVICE_OUTPUT_DATA);
IDataUtil.remove(reduceServicePipelineCur,
MapReduceConstants.IS_LAST_REDUCE_STEP);
reduceServicePipelineCur.destroy();
//将最终结果合并成管道数据
IDataUtil.merge(reduceIntermediateOutput,
mapperPipeline);
break;
}
}
}
final String tabSpace=″ ″;
//打印映射任务和化简任务的状态
System.out.println(″Num Map Tasks:″+mapTaskID+tabSpace+
″Num Reduce Tasks:″+reduceTaskID);
//作业完成即清理
InvokeState.setCurrentState(null);
InvokeState.setCurrentSession(null);
jobClient.clusterHosts.freeClusterHosts();
synchronized(this){
isJobDone=true;
this.notifyAll();
}
}
}
可以看出,在此示例中JobInProgress的run()方法包括用于处理输入文件的主代码,即分割(参见第18页第32行)、执行“并行化”的flow服务(参见第19页第8行)和化简(参见第20页第17行)的步骤。
执行映射的MapTask的示例性实现在以下代码列表中示出:
package com.wm.app.b2b.server.mapred;
import com.wm.app.b2b.server.InvokeState;
import com.wm.app.b2b.server.ThreadManager;
import com.wm.data.IData;
import com.wm.lang.ns.NSName;
public class MapTask extends AbstractTask implements Runnable
{
public MapTask(int id,TaskTracker taskTracker,NSName mapSvcName,
TaskResultStoreConfig policy)
{
super(id,taskTracker,mapSvcName,policy);
}
public void runTask()
{
ThreadManager.runTarget(this);
}
public void run()
{
InvokeState.setCurrentState(invokeState);
InvokeState.setCurrentSession(session);
taskStatus=new TaskStatus();
IData output=null;
try {
output=RPC.remoteInvoke(hostSelector.getHostInfoEntry(),
serviceName,taskInput);
taskInput=null;
}catch(Exception e){
output=null;
}
storeTaskResult(output);
synchronized(taskTracker){
taskTracker.addMapTaskToCompletedList(this);
setTaskStatus(TaskStatus.TASK_COMPLETE);
taskTracker.notifyAll();
}
InvokeState.setCurrentState(null);
InvokeState.setCurrentSession(null);
//System.out.println(″map task time is:″+toalSvcTime);
}
}
可以看出,当MapTask被执行时,即,当其run()方法被调用时,MapTask调用RPC类的remoteInvoke()方法(参见第23页第32行),该remoteInvoke()方法取得三个输入参数:hostSelector.getHostInfoEntry()、serviceName和taskInput。taskInput是从超类AbstractTask继承来的属性并且优选地包括要处理的输入,例如EDI文档的数据。
RPC及其remoteInvoke()方法的示例性实现在以下代码列表中示出:
package com.wm.app.b2b.server.mapred;
import com.wm.app.b2b.client.Context;
import com.wm.app.b2b.client.ServiceException;
import com.wm.app.b2b.server.ACLManager;
import com.wm.app.b2b.server.BaseService ;
import com.wm.app.b2b.server.Service;
import com.wm.app.b2b.server.invoke.InvokeManager;
import com.wm.app.b2b.server.ns.Namespace;
import com.wm.lang.ns.NSName;
import com.wm.data.IData;
import com.wm.data.IDataCursor;
import com.wm.data.IDataUtil;
public class RPC
{
private static final String DEFAULT_RPC_SERVER=″localhost″;
private static final int DEFAULT_RPC_PORT=5555;
private static final String DEFAULT_RPC_UID=″Administrator″;
private static final String DEFAULT_RPC_PASSWD=″manage″;
private static String rpcServer=null;
private static int rpcPort=-1;
private static String rpcUID=null;
private static String rpcPasswd=null ;
private static Objectlock=new Object();
public static IData remoteInvoke(HostInfo hostInfoEntry,NSName
mapTaskName,IData input)throws Exception
{
if(hostInfoEntry==null){
return null;
}
boolean isPrimary=hostInfoEntry.isPrimary();
if(isPrimary){
return Service.doInvoke(mapTaskName,input);
}else {
Context context=null;
//在这里同步,否则可能为主机创建不止一个上下文
synchronized (lock){
if(!hostInfoEntry.isConnected){
String hostName=hostInfoEntry.getHostName();
int port=hostInfoEntry.getPort();
context=new Context();
context.connect(hostName+″:″+port,DEFAULT_RPC_UID,DEFAULT_RPC_PASSWD);
hostInfoEntry.setContext(context);
hostInfoEntry.setConnected(true);
}else {
context=hostInfoEntry.getContext();
}
}
if(context!=null){
return context.invoke(mapTaskName,input);
}
}
return null;
}
}
ReduceTask的示例性实现在以下代码列表中示出:
package com.wm.app.b2b.server.mapred;
import com.wm.app.b2b.server.Service;
import com.wm.data.IData;
import com.wm.data.IDataCursor;
import com.wm.data.IDataUtil;
import com.wm.lang.ns.NSName;
public class ReduceTask extends AbstractTask
{
private Integer[] mapTaskIDArray=null;
private boolean inMemory=false ;
public ReduceTask(int id,TaskTracker taskTracker,NSName
reduceSvcName,TaskResultStoreConfig resultStoragePolicy)
{
super(id,taskTracker,reduceSvcName,resultStoragepolicy);
if(resultStoragePolicy instanceof TaskResultMemStoreConfig){
inMemory=true ;
}
}
public void setCompletedMapTasks(Integer[]mapTaskIDArray){
this.mapTaskIDArray=mapTaskIDArray;
}
public void runTask()
{
if(mapTaskIDArray==null || mapTaskIDArray.length==0){
return;
}
if(inMemory){
invokeReduceService(mapTaskIDArray);
}
else {
IDataCursor reduceServicePipelineCur=
taskInput.getCursor();
boolean isLastReduceBatch=
IDataUtil.getBoolean(reduceServicePipelineCur,
MapReduceConstants.IS_LAST_REDUCE_STEP);
IDataUtil.remove(reduceServicePipelineCur,
MapReduceConstants.IS_LAST_REDUCE_STEP);
reduceServicePipelineCur.destroy();
for(int i=0;i<mapTaskIDArray.length;i++){
if(i==mapTaskIDArray.length-1 &&
isLastReduceBatch){
reduceServicePipelineCur=
taskInput.getCursor();
IDataUtil.put(reduceServicepipelineCur,
MapReduceConstants.IS_LAST_REDUCE_STEP,true);
reduceServicepipelineCur.destroy();
}
invokeReduceService(newInteger[]
{mapTaskIDArray[i]});
}
}
storeTaskResult(taskInput);
}
private void invokeReduceService(Integer[]mapTaskIDs){
IData[]reduceInputDataArray=null;
IDataCursor reduceServicePipelineCur=taskInput.getCursor();
IData reduceIntermediateOutput=
(IData)IDataUtil.get(reduceServicePipelineCur,
MapReduceConstants.SERVICE_OUTPUT_DATA);
int length=0;
if(reduceIntermediateOutput!=null){
length=mapTaskIDs.length+1;
}else {
length=mapTaskIDs.length;
}
int count=0;
reduceInputDataArray=new IData[length];
if(reduceIntermediateOutput!=null){
reduceInputDataArray[count++]=reduceIntermediateOutput;
}
for(Integer mapTaskID:mapTaskIDs){
synchronized(taskTracker){
reduceInputDataArray[count++]=
taskTracker.removeMapTask(mapTaskID).getTaskResult();
}
}
IDataUtil.put(reduceServicePipelineCur,
MapReduceConstants.REDUCE_INPUT_DATA,reduceInputDataArray);
if(reduceInputDataArray!=null){
try{
Service.doInvoke(serviceName,taskInput);
}catch(Exception e){
e.printStackTrace();
}
}
reduceInputDataArray=null;
IDataUtil.remove(reduceServicepipelineCur,
MapReduceConstants.REDUCE_INPUT_DATA);
reduceServicePipelineCur.destroy();
}
}
MapTask和ReduceTask都以抽象类AbstractTask作为超类,即它们继承其属性以及set和get方法,这在以下AbstractTask的示例性代码列表中示出:
package com.wm.app.b2b.server.mapred;
import com.wm.app.b2b.server.InvokeState;
import com.wm.app.b2b.server.Session;
import com.wm.data.IData;
import com.wm.lang.ns.NSName;
public abstract class AbstractTask implements Task
{
private int taskID=-1;
protected TaskStatus taskStatus=null;
protected TaskTracker taskTracker=null;
protectedInvokeState invokeState=null;
protected Session session=null;
protected IData taskInput=null;
protected NSName serviceName=null;
protected HostSelector hostSelector=null;
private TaskResultStoreConfig taskResultStoreCfg=null;
public AbstractTask(int id,TaskTracker taskTracker,NSName
serviceName,TaskResultStoreConfigtaskResultStoreCfg){
this.taskID=id;
this.taskTracker=taskTracker;
this.serviceName=serviceName;
this.taskResultStoreCfg=taskResultStoreCfg;
}
public void setInvokeState(InvokeState invokeState)
{
this.invokeState=invokeState;
}
public void setInvokeSession(Session session)
{
this.session=session;
}
public void setTaskID(int taskID)
{
this.taskID=taskID;
}
public int getTaskID()
{
return taskID;
}
public void setTaskInput(IData taskInput){
this.taskInput=taskInput;
}
public void setTaskStatus(int status)
{
taskStatus.setTaskStatus(status);
}
public void storeTaskResult(IData result){
this.taskResultStoreCfg.storeResult(result);
}
public IData getTaskResult(){
return this.taskResultStoreCfg.fetchResultAndDestroy();
}
public void setHostSelector(HostSelector hostSelector)
{
this.hostSelector=hostSelector;
}
}
可以看出,AbstractTask本身实现接口Task,其示例性实现在以下代码列表中示出:
package com.wm.app.b2b.server.mapred;
import com.wm.app.b2b.server.InvokeState ;
import com.wm.app.b2b.server.Session;
import com.wm.data.IData;
public interface Task
{
public void setInvokeState(InvokeState invokeState);
public void setInvokeSession(Session session);
public void setHostSelector(HostSelector hostSelector);
public intgetTaskID();
public void setTaskID(int taskID);
public void setTaskStatus(int status);
public void setTaskInput(IData taskInput);
public void runTask();
public void storeTaskResult(IData result);
public IData getTaskResult();
}
在本发明的示例性实现中需要若干个其他基础类和接口,它们在以下代码列表中示出:
package com.wm.app.b2b.server.mapred;
import java.util.ArrayList;
import java.util.HashMap;
public class TaskTracker
{
private ArrayList<Integer>completedMapTasks=new
ArrayList<Integer>();
private ArrayList<Integer>completedReduceTasks=new
ArrayList<Integer>();
private HashMap<Integer,MapTask>mapResults=new HashMap<Integer,
MapTask>();
private HashMap<Integer,ReduceTask>reduceResults=new
HashMap<Integer,ReduceTask>();
public ArrayList<Integer>getCompletedMapTasks()
{
ArrayList<Integer>ret=completedMapTasks ;
//清除原始列表,以便我们只保留新信息
completedMapTasks=new ArrayList<Integer>();
return ret;
}
public int getNumCompletedMapTasks()
{
return completedMapTasks.size();
}
public void addMapTaskToCompletedList(MapTask task)
{
completedMapTasks.add(task.getTaskID());
mapResults.put(task.getTaskID(),task);
}
public MapTask removeMapTask(Integer taskID)
{
return mapResults.remove(taskID);
}
public ArrayList<Integer>getCompletedReduceTasks()
{
ArrayList<Integer>ret=completedReduceTasks;
//清除原始列表,以便我们只保留新信息
completedReduceTasks=new ArrayList<Integer>();
return ret;
}
public int getNumCompletedReduceTasks()
{
return completedReduceTasks.size();
}
public void addReduceTaskToCompletedList(ReduceTask task)
{
completedReduceTasks.add(task.getTaskID());
reduceResults.put(task.getTaskID(),task);
}
public ReduceTask removeReduceTask(Integer taskID)
{
return reduceResults.remove(taskID);
}
}
package com.wm.app.b2b.server.mapred;
/**
*TaskFactory利用以下配置创建MapTask或ReduceTask
*1.若标志isPersistMapIntResult和isStoreMapIntResult被设置,中间映射输出将被
*持续保留在$IS-DIR/logs目录中。文件名为Map_jobId_taskId或Reduce_jobId_taskId
*2.如果标志isPersistMapIntResult未被设置而标志isStoreMapIntResult被设置,则
*中间映射输出将被保存在存储器中。
*3.对于所有其他组合,中间映射输出不被存储在文件/存储器中
*/
public class TaskFactory{
public static MapTask createMapTask(JobInProgress job,int taskId){
String tag=″Map_″+job.jobClient.jobId+″_″+taskId;
if(job.jobClient.isPersistMapIntResult){
if(job.jobClient.isStoreMapIntResult){
return new MapTask(taskId,job.taskTracker,
job.jobClient.mapTaskName,new TaskResultFileStoreConfig(tag));
}
else {
return new MapTask(taskId,job.taskTracker,
job.jobClient.mapTaskName,new TaskResultNoStoreConfig(tag));
}
}
else{
if(job.jobClient.isStoreMapIntResult){
return new MapTask(taskId,job.taskTracker,
job.jobClient.mapTaskName,new TaskResultMemStoreConfig(tag));
}
else{
return new MapTask(taskId,job.taskTracker,
job.jobClient.mapTaskName,new TaskResultNoStoreConfig(tag));
}
}
}
public static ReduceTask createReduceTask(JobInProgress job,int
taskId){
String tag=″Reduce_″+job.jobClient.jobId+″_″+taskId;
return new ReduceTask(taskId,job.taskTracker,
job.jobClient.reducer,new TaskResultMemStoreConfig(tag));
}
}
package com.wm.app.b2b.server.mapred;
import com.wm.app.b2b.server.InvokeState;
import com.wm.app.b2b.server.Session;
public class TaskInProgress
{
public void executeTask(Task task,InvokeState invokeState,Session
session)
{
task.setInvokeState(invokeState);
task.setInvokeSession(session);
task.runTask();
}
}
package com.wm.app.b2b.server.mapred;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import com.wm.app.b2b.server.Server;
import com.wm.data.IData;
import com.wm.util.coder.IDataXMLCoder ;
import com.wm.util.coder.XMLCoderWrapper ;
public class TaskResultFileStoreConfig implements TaskResultStoreConfig{
private File savedFile=null;
private String dataTag=null;
public TaskResultFileStoreConfig(String dataTag){
this.dataTag=dataTag;
}
public IData fetchResultAndDestroy(){
InputStream is=null;
try {
XMLCoderWrapper xc=new XMLCoderWrapper();
is=new FileInputStream(savedFile);
return xc.decode(is);
} catch (Exception e){
e.printStackTrace();
} finally {
try{
if(is !=null){
is.close();
}
} catch(Throwable t){
t.printStackTrace();
} finally {
savedFile.delete();
}
}
return null;
}
public void storeResult(IData result){
try {
savedFile=new File(Server.getLogDir(),dataTag+″.xml″);
IDataxMLCoder xc=newIDataXMLCoder();
xc.writeToFile(savedFile,result);
} catch(Exception e){
e.printStackTrace();
}
}
}
package com.wm.app.b2b.server.mapred;
import com.wm.data.IData;
public class TaskResultMemStoreConfig implements TaskResultStoreConfig{
private IData result=null;
private String dataTag=null;
public TaskResultMemStoreConfig(String dataTag){
this.dataTag=dataTag;
}
public IData fetchResultAndDestroy(){
return result;
}
public void storeResult(IData result){
this.result=result;
}
}
package com.wm.app.b2b.server.mapred;
import com.wm.data.IData;
import com.wm.data.IDataFactory;
public class TaskResultNoStoreConfig implements TaskResultStoreConfig{
private String dataTag=null;
public TaskResultNoStoreConfig(String dataTag){
this.dataTag=dataTag;
}
public IData fetchResultAndDestroy(){
returnIDataFactory.create();
}
public void storeResult(IData result){
}
}
package com.wm.app.b2b.server.mapred;
import com.wm.data.IData;
public interface TaskResultStoreConfig{
public void storeResult(IData result);
public IData fetchReSultAndDestroy();
}
package com.wm.app.b2b.server.mapred;
public class TaskStatus
{
public static final int TASK_DISPATCHED=1;
public static final int TASK_RUNNING=2;
public static final int TASK_COMPLETE=3;
private int taskStatus=0;
public int getTaskStatus()
{
return taskStatus;
}
public void setTaskStatus(int taskStatus)
{
this.taskStatus=taskStatus;
}
}
package com.wm.app.b2b.server.mapred;
public class RoundRobinHostSelector implements HostSelector
{
private ClusterHosts clusterHosts=null;
private int hostIndex=0;
private Object lock=new Object();
public void setClusterHosts(ClusterHosts clusterHosts)
{
this.clusterHosts=clusterHosts;
}
public synchronized HostInfo getHostInfoEntry()
{
HostInfo hostInfoEntry=null;
synchronized(lock){
if(clusterHosts.hostInfoArrayList.size()==0){
return null;
}
hostInfoEntry=clusterHosts.hostInfoArrayList.get(hostIndex);
hostIndex++;
if(hostIndex==clusterHosts.hostInfoArrayList.size()){
hostIndex=0;
}
}
return hostInfoEntry;
}
}
package com.wm.app.b2b.server.mapred;
public class MapReduceConstants
{
public static final String SERVICE_INPUT_DATA=″serviceInputData″;
public static final String IS_LAST_SPLIT =″isLastSplit″;
public static final String MAP_ITERATION=″$mapIteration″;
public static final String IS_LAST_REDUCE_STEP=″isLastReduceStep″;
public static final String REDUCE_INPUT_DATA=″reduceInputData″;
public static final String SERVICE_OUTPUT_DATA=″serviceOutputData″;
public static final String SERVER_ID=″id″;
public static final String SERVER_HOST=″host″;
public static final String SERVER_PORT=″port″;
public static final String IS_PRIMARY=″isPrimary″;
public static final String MAPPER=″mapper″;
public static final String REDUCER=″reducer″;
public static final String MAPTASKNAME=″mapTaskName″;
public static final String THROTTLE=″throttle″;
}
package com.wm.app.b2b.server.mapred;
import java.io.File;
import java.io.Fi leInputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import com.wm.app.b2b.server.Server;
public class JobConfiguration
{
private static final String CLUSTERED_HOSTS_FILE_NAME=
″ClusterHosts.cnf″;
public void readJobConfiguration(ClusterHosts clusterHosts)
{
try{
//不改变的默认值。
InputStream fis=new FileInputStream(new File
(Server.getConfDir(),CLUSTERED_HOSTS_FILE_NAME));
Properties prop=new Properties();
prop.load(fis);
Enumeration propertyNames=prop.propertyNames();
while(propertyNames.hasMoreElements()){
String key=(String)propertyNames.nextElement();
StringTokenizer values=new
StringTokenizer(prop.getProperty(key),″;:″);
clusterHosts.addNodeToCluster(key,
values.nextToken(),Integer.parseInt(values.nextToken()),
Boolean.parseBoolean(values.nextToken()));
}
}catch(Throwable t){
//待做事项
t.printStackTrace();
System.out.println(″Error while reading the clustered
hosts properties file″);
}
}
}
package com.wm.app.b2b.server.mapred;
public interface HostSelector
{
public void setClusterHosts(ClusterHosts clusterHosts);
public HostInfo getHostInfoEntry();
}
package com.wm.app.b2b.server.mapred;
import com.wm.app.b2b.client.Context;
public class HostInfo
{
boolean isPrimary=false;
String hostDnsName=null;
int port=-1;
boolean isConnected=false;
Context context=null;
public HostInfo(String hostDnsName,int port,boolean isPrimary)
{
this.hostDnsName=hostDnsName;
this.port=port;
this.isPrimary=isPrimary;
}
public String getHostName()
{
return hostDnsName;
}
public int getPort()
{
return port;
}
public boolean isPrimary()
{
return isPrimary;
}
public boolean isConnected()
{
return isConnected;
}
public void setConnected(boolean isConnected)
{
this.isConnected=isConnected;
}
public Context getContext()
{
return context ;
}
public void setContext(Context context)
{
this.context=context;
}
}
package com.wm.app.b2b.server.mapred;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.LinkedHashMap;
import com.wm.app.b2b.client.Context;
public class ClusterHosts
{
protected Map<String,HostInfo>clusterMap=new LinkedHashMap<String,
HostInfo>();
protected ArrayList<HostInfo>hostInfoArrayList=new
ArrayList<HostInfo>();
public void addNodeToCluster(String nodeName,String hostDnsName,int
port,boolean isPrimary)
{
HostInfo hostInfo=new HostInfo(hostDnsName,port,isPrimary);
clusterMap.put(nodeName,hostInfo);
hostInfoArrayList.add(hostInfo);
}
public void freeClusterHosts()
{
clusterMap.clear();
Iterator iter=hostInfoArrayList.iterator();
Context context=null;
while(iter.hasNext()){
HostInfo hostInfo=(HostInfo)iter.next();
if(hostInfo!=null){
if(!hostInfo.isPrimary){
context=hostInfo.getContext();
context.disconnect();
hostInfo.setContext(null);
hostInfo.setConnected(false);
}
}
}
hostInfoArrayList.clear();
}
}