Welcome 微信登录

首页 / 软件开发 / JAVA / 构建自己的基于Java的超级计算机

构建自己的基于Java的超级计算机2010-05-21如果您曾想过构建自己的超级计算机,但却对用进行并行编程望而生畏,那么伪远程线程可以帮您解决这一问题。这种获奖的编程模型极大地简化了集群上的并行编程,并使超级计算走出实验室,使每一位 Java 程序员都能使用它。

在过去的三年里,并行集群已在改变着超级计算的面貌。一旦价值数百万美元的单体机占了主导,并行集群很快就会成为超级计算机的选择。可以想像得到,开放源码圈内的高涨热情已导致产生了数百 -- 如果不是数千的话 -- 并行集群项目。第一个同时也是最著名的开放源码集群系统是 Beowulf。在NASA 赞助下,由 Thomas Sterling和Donald Becker 在1994 年发布的Beowulf,开始是作为一个 16 节点演示集群推出的。今天,Beowulf 已有数百种实现,从Oak Ridge 国家实验室的Stone SouperComputer到Aspen 系统公司的定制构建的商业性集群(请参阅参考资源)。

对 Java 程序员不利的是,多数集群系统都是围绕基于C语言的软件消息传递 API — 如消息传递接口(MPI)或并行虚拟机(PVM)— 来实现的。用C语言进行并行编程不是件容易的事,因此我设计了一个替代方案。本文将说明如何综合运用Java 线程和 Java 远程方法调用(RMI)来创建自己的基于Java的超级计算机。

请注意,本文假定您有 Java 线程和 RMI的应用知识。

超级计算机内有什么?

超级计算机的定义是:由八个或更多的节点组成、作为单个高性能机器工作的集群。基于Java的超级计算机包含一个作业调度器和任意数量的运行服务器(也称为主机)。作业调度器生成多个线程,每个线程包含执行不同子任务的代码。各个线程将其代码迁移到不同的运行服务器上。然后,每个运行服务器执行迁移给它的代码并将结果返回给作业调度器。最后,作业调度器将各个线程的结果组合起来。

这种并行集群系统之所以被称为伪远程线程,是因为线程是在作业调度器上调度的,但线程内的代码却是在远程计算机上执行的。

该系统有哪些组件?

组件一词是指组成“伪远程线程”并行集群系统的逻辑模块。该系统包含以下组件:

Job dispatcher(作业调度器) 是执行控制的机器。它生成不同的线程,每个线程都包含此集群要处理的主任务的一个子任务。每个线程内的代码都被发送到一台远程计算机去执行。线程在作业调度器上调度,所以理论上讲,该机器不应该用于执行任何子任务。

SubTask 是一个用户定义类,该类定义主任务的一个数据或功能独立的部分。您可以为主任务的不同部分定义不同的类。类名 SubTask 是一个示例。您可以为一个 SubTask类取任何名字,不过这个名字应该能描述分配给它的子任务。在定义SubTask类时,您必须实现JobCodeInt接口以及 jobCode()方法,下面对其进行说明。

JobCodeInt 是一个 Java接口。您必须在定义子任务的类中实现该接口和 jobCode()方法。jobCode()方法描述了将在远程执行的代码。如果您打算在远程使用某个本地资源,您必须在jobCode()方法外部初始化这个资源。比方说,您要将一组图像发送到远程处理,就必须在jobCode()方法外部初始化 Image 对象。您可以在该方法中调用标准 Java 库中的类,因为远程计算机上存在这些库。

RunServer 是一个对象,该对象允许远程过程调用其方法。它的一个方法以实现了JobCodeInt接口的对象作为参数。 RunServer 就在运行该对象的计算机(运行服务器)上执行该对象内的代码,并将计算结果作为 Object类的一个实例返回。Object 是 Java类层次结构中最高一级的类。

PseudoRemThr 是一个 Java类,该类封装了一个线程并接受给定 SubTask类的一个实例。它选择一台远程主机,并将 SubTask 实例发送到这台主机上执行。如果您要利用某台主机上可用的特定资源(诸如数据库或是打印机),则可以指定主机。

HostSelector 是一个模块。如果您没有指定远程主机,PseudoRemThr类就会调用HostSelector 模块来选择特定的主机。如果没有空闲的主机,HostSelector 会返回负载最小的远程计算机。如果某个远程计算机是一个多处理器系统,HostSelector 可能会不止一次地返回该主机名。目前,HostSelector 无法根据给定任务的复杂程度来选择主机。

伪远程线程的工作方式

要使用伪远程线程,您必须实现作业调度器和运行服务器。本节将说明如何实现各个部分。

实现作业调度器

首先,将主任务分解为数据或功能独立的子任务。针对每个子任务,定义一个实现JobCodeInt接口(从而实现jobCode()方法)的类。在jobCode()方法中,定义各给定子任务要执行的代码。

请注意,您不能调用作业调度器上用户定义的的本地资源。请在该方法外部初始化所有这类资源。例如,您可以在SubTask类的构造函数中初始化这类资源。

创建类 PseudoRemThr的若干实例,并将 SubTask的实例传递给 PseudoRemThr的各个实例。如果您要明确指定一台远程主机,您可以通过调用PseudoRemThr 对象的另一个构造函数来完成。

等待这些线程完成。调用getResult()方法来获取 PseudoRemThr的各个实例的执行结果。如果计算没有完成,结果返回一个值为 false的Boolean 对象;否则,将返回 Object类的一个实例,其中包含了计算结果。您必须将此实例转换为您所希望的类类型。将所有的子任务结果组合为最终结果。

实现运行服务器

实现运行服务器是一项简单的工作:

启动 RMI 注册程序。

启动 RunServer。

运行服务器在启动时接通作业调度器,并通知作业调度器它已准备就绪,可以接受要执行的任务了。