Java RMI 服务器框架[转IBM]


署理逻辑进程同步的完成,并且没有任何返回数据署理队列应用程序处理类是放向后调用向前调用或处理哀求完成的任何其它的必要逻辑的地方


既然我们已为公共内存成立了一个锚,那么我们还需要将其他类链到锚上用这种体例,任何 RMI 连接线程(在 清单 2中是与实现类关联的线程)都可以找到应用程序线程

当 syncRequest() 在等待时, 每个应用程序线程:



public final class FrameWorkBase { // Function Array public static FuncHeader func_tbl = null; // Remote Object myself (for recursive calls) public static FrameWorkInterface fwi = null;

RMI 服务器启动(在 清单 4 中是 FrameWorkServer 类)实例化公共内存(类 FrameWorkBase )的基础,并分配一个类域给 FrameWorkBase 对象的引用因为引用是举动的,而且服务器是长期的,所以 JVM 不垃圾收集对象考虑将启动类字段当作锚点

高容量的可恢复的安全的可办理的软件系统的基本组件(最初与事务处理服务器一起引入)是同步(或后端)处理同步处理的基本流有如下步骤:

通过任何或者的体例来把哀求放到举措措施中 把哀求分成组件部分,并把那些组件放入队列 使用线程(任务)来并行处理队列,从而减少响应时候和开消 为下一个可用的输出处理器而把回应放进队列

隶属于同步处理的基本构件是队列和线程结构把队列和线程加到 RMI 服务器(一个同步的实体),然后您将得到一个有任务本领的服务器最好的部分是那并不是很难做到的

RMI 服务器在计算机中作为独立的进程因为该进程和客户机进程可以彼此独立的运行,所以交互是同步的同步的进程需要能使其自恃的所需的某一程度的办理 框架

级别: 初级


公共内存的基础是 FrameWorkBase 类(参睹清单 8)该类包含对其它类的 静态引用对于此示例来说,该域是公共的(这仅是成立公共内存的一种体例)

首先,我们来研究单组件同步进程办理器的队列和线程环境然后,我们将此单组件环境转换为可以并行处理多个队列的哀求署理

同步对同步

把 GUI 应用程序看作同步应用程序单击按钮,逻辑就在那里处理事件 RMI 是完全同步的应用程序逻辑与接口位于不同的 Java 虚拟机(JVM)您需要发送此哀求并接收来自另一 JVM 的回答

您应该稍微熟悉一下 RMI,至少是已读了一些在 参考资料部分提到的 RMI 教程这是个困难的题目,所以我们一次只做一小步有时您需要看一下演示来掌握要点;本文中我们有一个演示而且有时您需要查阅代码来真正了解正在做什么;我们也提供了代码自然的,您应该熟悉同步处理和同步处理的不同

为什么 EJB 和 GUI 应用程序会如此成功?因为它们在容器 框架(用于办理长期性消息传递线程办理日志记录事件队列用户界面等等,还有很多)内运行

监控同步进程

RMI 运行时是 Java 设计师的一个伟大创作对于大多数实现来说,RMI 运行时实例化一个线程来处理来自客户机的每个哀求一旦哀求完成了,线程为下一个客户机哀求等待短久的时候通过这种方法,RMI 连接可以重用线程假如没有新的哀求进来,RMI 运行时就销毁这个线程

开发者有时会忘记 RMI 运行时不是而且也从不应该是成熟的应用程序服务器使用基本的无应用程序服务的 RMI 运行时会使开发变难考虑这两个异常基础的问题:

实用的解决方案

对这些问题和更多的问题的实用的解决方案是从应用程序处理中分离 RMI 连接举动您可以通过在服务器端创建一个应用程序队列和线程结构来做到这一点这种是高可靠的完全任务重要的软件产品的工作体例,而且该结构适用于任何应用程序

想象适用该模型对于需要已计时响应的客户机,RMI 连接线程与应用程序线程联系假如应用程序线程没有在时候限制内响应,那么 RMI 连接线程向客户机返回一个超时消息对于自主的哀求,RMI 连接线程联系应用程序线程,并立即返回客户机一个已被调度的消息

应用程序线程

RMI 连接线程和应用程序线程可以彼此对话 应用程序环境可以知道客户机超时和恢复 不会出现线程过载问题当有太多的应用程序线程在执行以至于 JVM 不能再承担更多的线程时,或是当很多的应用程序线程引起太多的资源竞争以至于环境有效的死锁时,这样的过载就或者会发生 应用程序线程创建/销毁开消没有使应用程序处理陷入泥沼 整个线程环境可以得体的封闭

我们将研究您可以运行的同步进程办理器框架您可以从 参考资料部分下载这些类来执行,也可以下载到源代码

框架的基础

与任何软件系统一样,框架是由基础的构件组成的整个结构最初看起来也许很复杂,但它其实不过是成立在彼此之上的组件的共同工作而已在接下来的几节中,我们将讨论框架的五个不可缺的元素

因为启动类将对 FrameWorkBase 的引用传递给实现类的组织函数(在 清单 2 中是 FrameWorkImpl 类),而且实现类将 FrameWorkBase 引用保存在它的实例域,所有的 RMI 连接线程都可以访问 FrameWorkBase 类


图 4.公共内存引用
Java RMI 服务器框架[转IBM] - David - David的博客

对于任何的哀求,RMI 连接线程:



// requestor obj (monitor) to cancel wait Object requestor = this; // integer array created by RMI-Connection thread pnp = new int[1]; pnp[0] = 0; // passed object from the Client Object input_from_client = client_data; // returned object from the application thread Object back;

requestor :为使用对象等待和对象关照方法,RMI 连接线程和应用程序线程都必须可以访问实现类 Object
input_from_client :这是在参数(参睹 清单 3)中传递给 RMI 服务器的客户机对象



一个逻辑的预排

逻辑进程的起源

逻辑进程这个术语来自事务处理服务器领域客户机事务把哀求放进后端队列,并让同步任务处理该队列任务独立于其它的非后端的任务工作 但它们不是物理分离的,只是逻辑上分离的

考虑一个逻辑进程如在 Amazon.com 完成订单举动您上线并下定单因为 Amazon 没有专门针对您的收定单的人,所以您的定单进入定单队列您继续您的业务在 Amazon 仓库,某个人从先进先出(FIFO)定单队列拿到您的定单并添补定单假如您想要一本书,也许定单就是由一个特定仓库的一些可以填写定单的雇员填写的假如您想要一个盒式录像带,也许定单是由另一个不同的仓库的一些不同的雇员填写的,根据要求仓库也许更大,也许更小这个定单完成流程是个逻辑进程

软件中的被办理的进程环境行为相似在 Amazon,填写定单的是雇员在软件系统中,填写定单的是应用程序线程

也许冗长而难读吧此刻,在设法消化所有的这些信息之前,我们需要提出每个项目的三个重要的问题:


图 1. 逻辑进程
Java RMI 服务器框架[转IBM] - David - David的博客

概述同步进程办理器如何使用逻辑进程
客户机向服务器发送哀求RMI 运行时创建或重用一个 RMI 连接线程,该线程执行实现类中的一个方法恰当的方法会把哀求放进队列中的劣先等待列表,并且使用对象关照方法唤醒应用程序线程来为哀求服务这里有两种或者的哀求:

类对象的等待/关照方法

wait() 使当前的线程等待直到:或者另一个线程为此对象调用了 notify() 方法,或者过了必然长度的时候 notify() 唤醒正服侍该对象的管程的单一的线程线程通过调用一个等待方法来服侍一个对象的管程

来看一下同步进程办理器的不同部分,它们使事情更逼真关键组件是我们需要使用 RMI 的基本的东西和我们需要实现队列/线程模型的特定于类的应用程序

任何同步进程的一个额中的关键要求是监控逻辑进程的体例因为同步哀求或者会超时,所以需要有一个释放内存的过程自主哀求执行无须客户机等待它们的完成;当自主哀求不能完成时 换句话说,当它们停止时 很难检测出该停止

一个接口继承了 java.rmi.Remote 实现接口的声明的具体的实现类 将客户机信息传递给 RMI 服务器的参数类

接口对于该框架,远程接口(如清单 1 所示)需要至少三个方法:

处理定时哀求的 syncRequest() 方法 处理自主的哀求的 asyncRequest() 方法 客户机可以用来得体的封闭 RMI 服务器的 shutDown()
清单 1. 框架接口
public interface FrameWorkInterface extends Remote { public Object[] syncRequest(FrameWorkParm in) throws RemoteException; public Object[] asyncRequest(FrameWorkParm in) throws RemoteException; public String shutRequest() throws RemoteException;

具体的实现类
FrameWorkInterface 的实现是 FrameWorkImpl 类清单 2 是实例域和一部分组织器因为这是 RMI 连接线程逻辑所在的类(把哀求放进哀求队列,唤醒一个应用程序线程,将回应返回客户机),所以我们将在本文稍后的地方讨论内部的运作


清单 2. 框架实现类
public final class FrameWorkImpl extends UnicastRemoteObject implements FrameWorkInterface { // instance field (base of common memory) private FrameWorkBase fwb; // constructor public FrameWorkImpl (FrameWorkBase reference_to_fwb) throws RemoteException { // set common memory reference fwb = reference_to_fwb;

参数类这个类(如清单 3 所示)将客户机信息传递给 RMI 服务器实例域如下:

Object client_data :从客户机到应用程序处理类的可选对象
String func_name :逻辑进程的名称;在本文的后面部分也将其称为 函数 function
int wait_time :当使用 syncRequest() 时,RMI 连接线程等待应用程序线程完成的最大的秒数(即,超时候隔)
int priority :哀求的劣先级劣先级为 1 的哀求应该在劣先级为 2 的哀求前被选择,劣先级为 2 的哀求应该在劣先级为 3 的哀求前被选择,诸如此类
清单 3. FrameWorkParm 类
public final class FrameWorkParm implements java.io.Serializable { // passed data from Client private Object client_data; // Function name private String func_name; // maximum time to wait for a reply private int wait_time; // priority of the request private int priority;

特定于应用程序的要素
除了为 RMI 而需要的中,应用程序还要求我们成立如下的东西:

创建 RMI 服务器环境的启动类 定义劣先级等待列表应用程序线程和对应用程序处理类的引用的队列类 从等待列表取得哀求并调用应用程序处理类的线程类 执行应用程序逻辑的应用程序处理类

启动类该类,如清单 4 所示,启动 RMI 服务器它包含用于成立长期环境的逻辑


清单 4. 框架的启动类
public final class FrameWorkServer { // The base for all persistent processing private static FrameWorkBase fwb = null; // Start up Server public static void main(java.lang.String[] args){ // the base for all processing fwb = new FrameWorkBase(); // now, after initializing the other FrameWorkBase fields // including the application queues and threads, // do the Implementation class with a ref to FrameWorkBase FrameWorkImpl fwi = new FrameWorkImpl(fwb);

应用程序队列应用程序队列包含三个主要元素:

哀求挂起所在的劣先等待列表:当没有应用程序线程可以立即对哀求作出响应时,哀求将在等待列表中排队等待当线程完成对一个哀求的处理时,线程查看列表来获得下一个哀求这通过让每个线程在一个线程执行周期完成多个哀求而减少了机器的开消
应用程序线程的锚点:锚点不过是对实例化的线程的引用这也许与您更熟悉一些的线程池有所不同为每个线程使用特定的引用:
让我们轻易的在线程不执行时找到该线程,这样我们就可以杀死它或恢复并重新启动它 让我们轻易的捕获线程来调试 通过定义每个队列的线程的总数和仅当确实必要的时候才初始化线程的方法来限制线程间的争用并减少线程过载的问题 通过使队列对每个线程有固定的引用使保留统计异常简朴统计形成了调劣的基础
对应用程序处理类的引用:通常多部分的应用程序类(线程逻辑和应用程序逻辑)被分成了两个独立的类:一个应用程序线程类(只是线程逻辑)和一个应用程序处理类(只是应用程序逻辑)

对于该分离最初也许有点疑惑,因为大多数开发者将线程看作应用程序类的一部分处理除应用程序逻辑之中的线程逻辑需要将两种思维体例合并起来该框架设计将线程逻辑从应用程序逻辑分离(我们称为 应用程序处理类),从而应用程序逻辑能够轻易的插入线程结构(参睹清单 5)

仍然困惑?考虑您用来浏览本文的浏览器您愿意放首歌或看个录像吗?想想必须把那个逻辑放进基础产品的可怜的浏览器的开发者吧但假如该增加的逻辑是个插件,那么不仅基础代码小并轻易维护,而且您还可以在任何时候安装任何供应商的任何逻辑模块


清单 5. 队列类
public final class QueueHeader { private String que_name; // name of this queue private WaitLists waitlist; // pointer to wait lists private int nbr_waitlists; // number of wait lists private int nbr_wl_entries; // number of entries in a waitlist private int wait_time; // time to wait when no work private int nbr_threads; // total threads for this queue private QueueDetail[] details; // detail array of thread info // public application logic class for use by the Queue Thread public DemoCall to_call; // class to call

应用程序线程类
应用程序线程类包含线程逻辑每个线程区域(清单 6 中的 QueueDetail 类)包含指向实际线程的指针当一个 RMI 连接线程唤醒应用程序线程时,应用程序线程从队列的等待列表中取得哀求然后应用程序线程调用应用程序处理类的方法来执行工作并将从那个方法返回的对象传回给 RMI

每个线程区域( 清单 6 中的 QueueDetail 类)包含指向物理线程的指针RMI 连接线程使用物理应用程序线程的引用来调用一个同步方法,该方法发出一个对象关照来唤醒应用程序线程


清单 7. DemoCall 接口
public interface DemoCall { public Object doWork(Object input, FrameWorkInterface fwi) throws java.lang.Throwable;

看一下该应用程序的 doWork() 方法的两个参数,我们可以知道:

来自客户机的对 FrameWorkParm 实例的引用,以 Object 类型传递(参睹 清单 3)
对服务器自身( FrameWorkImpl )的引用,以 FrameWorkInterface 类型传递第二个引用是这样的,应用程序可以称服务器 作为一个客户机这是 递回,是编程中最有用的技术之一,而且有时也是最难实现的之一

公共内存

该框架后的基本原则是 公共内存的概念对于任何两个彼此通话的线程,必须使用它们之间是公共的内存没有一个线程拥有该内存;该内存是可被所有的线程是可更新和可睹的


仅因为在线程间是公共内存,并不意味着线程可以没有任何限制的访问或修改此内存这在任何语言中都是成立的 修改在多线程中使用的变量的关键是同步语句或同步方法修饰符在 Java 语言中,有共享的主内存和线程内存JVM 从共享主内存给每个线程它所需的一个变量的副本,并且将变量存回共享的主内存以供其它的线程使用这是 Java 的内存模型(您可以参阅 Java Language Specification 的第 17 章来获得更多的信息可以在 参考资料部分找到一个链接)把对一个对象的访问/改变放进一个同步块或方法可以完成三个功能:

同步
防止其它线程(在同一个对象上同步的)的执行 当线程访问变量时,从共享的主内存读取该变量的当前值到线程存储器 当线程改变了变量时,将线程存储器的变量的新值在块或方法的末尾写到共享的主内存 所以,为保证完整性和确保所有的线程都可以访问变量的最新值, 关于内存访问的基本文字,可以参阅 Brian Goetz 所著的Can Double-Checked Locking Be Fixed?另中,关于多 CPU 线程同步的深入的文章,请参阅 Allen Holub 著的Warning! Threading in a Multiprocessor World您可以在 参考资料部分找到这两篇文章的链接

RMI 服务器是长期的这意味着服务器所创建并且还没释放(举动引用)任何对象会为服务器的生命期内保留当服务器启动时,它获得一个公共内存类( 清单 8 中的 FrameWorkBase 类 )的新的实例,并且分配一个带有该引用的私有的静态的域RMI 实现类( 清单 2 中的 FrameWorkImpl 类)也获得对公共内存类的引用通过这种体例,所有的运行在服务器的线程 RMI连接线程和应用程序线程 都可以访问该公共的内存

图 2 是公共内存和使用它的线程的图示


图 2. 公共内存
Java RMI 服务器框架[转IBM] - David - David的博客

对,使用 Java 内存和垃圾收受接管是有点难了解我们即速将做演示然而,在我们开始前,我们需要讨论一下线程是如何彼此发现的

指针

Java 语言不支持指针计算 但这并不意味着 Java 语言中没有指针用 Java 中的说法,指针被称为 引用尽管您不能增加或减少引用,但您还是可以使用引用来定位其它实例化类沿着这个链走的过程就象第一台计算机一样老那些熟悉其它有指针的语言(如 C/C++)的人可以在下面的段落中识别出一个通用的技术

假如我们能扩展这个简朴的框架的话,由什么来支持每个哀求的多队列呢?当一个客户机哀求对资源的多访问时,假如我们能分割该哀求,并把每个组件放进它自己的队列,那么我们就可以并行处理哀求了此刻,这个或者性是无尽的这是 哀求署理(request brokering),它是下一节的主题

本文帮助您了解为什么同步进程需要办理,并概述了最后设计自己的定制的同步进程办理器的必要步骤

讨论

许多 Java 开发者都误认为他们可以现成的将远程方法调用(Remote Method Invocation(RMI))服务器用作成熟的应用程序服务器这是毛病的假定,并跟着开发的进展,它将导自极大的痛苦一个比较好的方法是成立一个围绕 RMI 的框架,由该框架为这样的成熟的应用程序服务器提供结构

然后,我们用框架来大致处理一个来自客户机的简朴的哀求

提到线程,大多数开发者都会畏缩;但处理线程是创建我们所要的这种有任务本领的服务器的核心有太多的关于线程的书和文章,以至于任何明智的人都会对多线程项目感到畏惧但事实上单功能的线程是轻易设计的处理应用程序的多线程的要点是将线程从应用程序分离,并单独的控制每个线程本文将为您演示如何去做

封闭


清单 9. 函数数组
Java 平台的设计师所设计的远程方法调用(Remote Method Invocation)运行时是个伟大的作品 但它并不打算作为成熟的应用程序服务器通过从应用程序处理分离 RMI 连接举动,您将可以省去大量的开发的辛苦和压力本文中,高级 Java 开发者 Edward Harned 介绍了一个框架,通过它您可以刚好达到目的您可以利用这里出现的代码或把这里的代码修改一下来满足您的应用程序的特定需要您可以通过单击本文的上部或下部的 来在 讨论论坛与作者和其他读者分享您对本文的看法
public final class FuncHeader { private int nbr_func; // number of functions private FuncDetail[] details; // detail entries

FuncHeader details 数组(相继的被搜检)包含一个元素代表每个框架所支持的函数(清单 10 中的 FuncDetail 类)



public final class FuncDetail { private String name; // Function name private long used; // times used private QueueHeader qtbl; // queue

每个 FuncDetail 包含:

函数的字符串名 对函数的队列的引用( 清单 5中的 QueueHeader 类)

每个队列(参睹 清单 5)包含:

通过搜索可用线程的详细信息数组,RMI 连接线程可以找到哀求的可用的线程区域( 清单 6 中的 QueueDetail 类)


清单 6. 线程区域类
public final class QueueDetail { private int status; // status of this entry private String tname; // name of this thread private int totl_proc; // total requests processed private int totl_new; // total times instantiated private FrameWorkBase fwb; // base storage private QueueHeader AH; // Queue this belongs to private QueThread thread; // queue thread

应用程序处理类
一个单独的应用程序处理类包含了应用程序逻辑我们前面所谈到的应用程序线程类调用关于应用程序处理类的方法对于本示例,我们使用 DemoCall 接口(在清单 7 中)实现该接口的任何类都是可以承受的

查找应用程序线程:一个普通的示例
我们已看到了很多的类和很多的指针 下面还有很多内容这里用中文做一番描述

当 client 调用一个远程方法时,RMI 连接线程(将对 FrameWorkBase 类的引用作为实例域)(参睹 清单 2):

使用该引用来定位 FrameWorkBase 类 使用 FrameWorkBase 实例域引用来定位函数数组 搜索函数数组来寻找想要的哀求的函数详细信息元素 使用函数详细信息队列引用来定位哀求的队列(在该示例中是 Queue2) 把哀求放进 Queue2 的等待列表 在 Queue2 中找到等待应用程序线程 使用对线程的引用(在本示例中是 Thread2)来 notify() 应用程序线程并等待它的完成

留意:最后一步使用 notify() 这并不是搞错了大多数关于线程的书籍都会建议您使用 notifyAll() notify() 和 notifyAll() 方法都是 Object 类的方法问题是:对于一个特定的 Object ,有多少线程呢?对于 Queue 情况,每个实例仅有一个线程:

QueueThread qt1 = new QueueThread(); QueueThread qt2 = new QueueThread();

Notify() 唤醒一个任意的线程但 qt1 和 qt2 域是引用因为每个 QueueThread 对象仅有一个线程,所以 notify() 起作用它还会唤醒一个任意的线程,但它仅有一个可供选择

图 3 说明了顺着该链来查找应用程序线程的过程



Java RMI 服务器框架[转IBM] - David - David的博客

查找 RMI 连接线程
当应用程序线程处理完了时,应用程序需要查找正调用的 RMI 连接线程以将其唤醒因为没有返回链,应用程序线程是如何知道谁调用它的呢?它不知道这就是为什么应用程序线程必须要使用 notifyAll() 以及为什么在 RMI 连接线程要做些额中的工作了

RMI 连接线程必须向应用程序线程传递些东西以唯一的标识自己RMI 连接线程所创建和通过引用传递给另一个线程的任何对象对于其它线程来说都是可用的在一个同步事件(记得与本文有关的 )后,然后每个线程都可以访问到对象的当前值了对于本示例,框架使用了一个整数数组(参睹清单 11)

在 RMI 连接线程为哀求找到适当的队列后,它把一个 增强的哀求放进队列的等待列表这是来自参数 FrameWorkParm (参睹 清单 3)和清单 11 中的几个其它的域的客户机的对象:

操作系统把 Java 虚拟机看作 进程,把线程看作 轻量级进程我们正研究的同步进程办理器的底层模型被称为 逻辑进程该模型看起来象什么呢?

当客户机调用一个远程方法时,RMI 连接线程:

自从对队列和子任务分配的学术介绍以来,Edward Harned 一直在积极的磨砺了他的多线程和多进程天资他最初是作为主要工业的雇员辅导项目,然后他做了独立的顾问如今,Ed 是 的一位高级开发者,在那里,最近四年中,他用 Java 编程为广泛的任务带来了同步处了解决方案您可以通过 与 Ed 联系

此刻,目标是设计一个队列和应用程序线程环境以便:

另一方面,当处理完成时,应用程序线程做以下的事情(参睹清单 13):



获得 RMI 连接的管程(通过在被传递的对象上同步) 将引用赋于来自应用程序处理类的任一返回对象 将等待 RMI 连接线程的整数数组中的第一个整数发出 为所有的 RMI 连接线程发出一个通用的唤醒( notifyAll() )
// get lock on RMI obj monitor synchronized (requestor) { // the object from the application processing back = data_object; // set posted pnp[0] = 1; // wake up all RMI-Connection threads requestor.notifyAll(); }

自主的哀求

RMI 服务器没有相似的应用程序容器来办理队列线程消息传递日志记录等等我们必须自己成立它

来自应用程序处理类( 清单 7)的 doWork() 方法的返回数据会发生些什么呢?它的返回数据到哪里去了并不该作为应用程序关注的事开发者必须能够为计时的或是自主的哀求使用相同的应用程序逻辑所以,我们需要一个自主哀求的署理

署理仅是另一个逻辑进程换句话说,它是个有关联线程的队列普通队列和署理队列的唯一不同在于 RMI 连接线程访问普通队列,而应用程序线程 可选的访问署理队列

署理

对于一个计时哀求来说,客户机从应用程序获取响应将署理考虑为一个服务器端的伪客户机:该署理从应用程序获取响应 这有点迷惑,但先放在那;最后这点会变清楚的当进入多部分哀求时,您将发现没有署理是无法存活的

应用程序处理类( doWork() 方法)可以返回一个对象到应用程序线程对于自主哀求来说,当想要时,应用程序线程可以通过创建一个新的增强的哀求(用刚返回的对象)将增强的哀求放进署理的等待列表并唤醒一个署理线程来激活一个署理程序逻辑进程这与 RMI 连接线程在根本上是相同的

摘要

RMI 运行时

基本的 RMI 要素
RMI 要求我们成立如下的东西:

Daemon(守护)线程

假如您使用 UNIX 类型的操作系统,那么您应该熟悉 daemon 线程这个术语Windows 类型的操作系统将其称为 terminate-and-stay(终止并存留)线程该线程起作用然后休眠直到下一次它被哀求

监控环境的一种体例是使用一个定期扫描环境的 daemon 线程把一个线程称为 daemon仅仅是意味着它不是特定的应用程序或 RMI 连接的一部分当监控线程发现问题时,它可以更正此问题,记录该问题的详细信息发送一个消息给中间件队列或内部的关照另一个远程对象该行为取决于应用程序,所以超出了本文的范围

一个 RMI 连接线程使用它的实例域引用来定位 FrameWorkBase 类(参睹 清单 8)然后该 RMI 连接使用 FrameWorkBase 实例域引用, func_tbl ,来访问函数数组(清单 9 中的 FuncHeader 类)

来计时 RMI 服务器哀求 来运行自主的 RMI 服务器哀求 把署理作为任何自主哀求的一部分来运行 来限制应用程序线程常睹的创建/销毁开消 来减少线程过载问题 来运行来自所有应用程序的递回哀求 来简便的调试哀求,尤其是自主哀求(我们知道工作所驻留的应用程序线程和应用程序类无须捕获无关的代码) 使用管程来找出未执行的哀求 得体的封闭 RMI 服务器

框架将 RMI 线程环境从应用程序线程环境分离另中,它还把应用程序线程从实际的应用程序逻辑分离这是在面向对象设计中的异常重要的 更高级的抽象

封闭 RMI 服务器的得体的体例是用一个封闭方法但是,假如封闭方法仅结束 Java Virtual Machine,那么该方法的返回消息从不会返回客户机更好的体例是启动一个封闭线程该线程休眠大约两秒的时候来给线程的返回消息一个排除虚拟机的机遇,然后发出 System.exit(0)

对后端服务的哀求在队列中排队等待每个 RMI 连接线程把客户机的哀求放进队列应用程序线程从队列中取得哀求,并对其做出反映开发者为不同类型的哀求定义队列(象在 Amazon 将放书的仓库和放盒式录像带的仓库分离),并且定义了为每个队列服务的应用程序线程的最大数目这些是逻辑进程

为回答第一个问题,框架为我们提供了本领:

跟随指导来运行多客户机线程, DemoClientMultiBegin 类,来跟随系统加载



参考资料

第三个问题的答案,哦,这很好,但也许投入所能得到的回报并不值得付出勤奋结构的关键的部分是毛病恢复有时异常规代码远比标准代码更重要框架所需的是存在的更充分的理由

RMI 连接是如何找到应用程序队列并唤醒一个应用程序线程的呢?应用程序线程又是如何唤醒 RMI 连接线程的呢?答案在于环境的结构和指针的使用

一旦我们成立了基本的队列环境,即速就很明显,一些哀求确实是包含了多个行为或组件例如,完成哀求也许会要求访问两个不同的数据库我们可以以线性体例来访问每个数据库,但是对第二个的访问要等到第一个完成后才行

哀求署理

在介绍逻辑进程时,我们使用了从 Amazon.com 订购书或盒式录像带的类比用多部分的哀求,书和盒式录像带我们一次就可以 订购到书部分的哀求进入书队列,盒式录像带部分进入盒式录像带队列两个队列都有自己的雇员(应用程序线程)来处理定单

处理多行为哀求的更好些的体例是把哀求分割成它的组件部件并把每个组件放入一个单独的队列这样就可以并行处理了这比线性编程要难,但是得到的益处远比预先的额中工作要重要

我们需要什么来支持哀求署理呢?我们要了解客户机哀求不再是仅被单一的逻辑进程所关注的所以,我们必须把客户机哀求放进一个通用的区域,从而任何数量的逻辑进程都可以访问它因为我们已有了一个公共内存环境,所以我们此刻必须将它增强

这是个简朴的部分困难的工作一直是成立结构中第一个基础的块在接下来的几节中,我们通过以下几种体例来增强单组件框架:

然后我们用新的框架来大致处理一个简朴的哀求

增强公共内存

我们需要一个公共的地方来安排来自客户机的同步哀求和同步哀求的详细信息该公共信息包括:

来自客户机的对象(在 清单 3中是 FrameWorkParm 类的 client_data 对象) 所有的其它增强哀求域

所需的全部就是一个简朴的对象数组本例中的对象是 SyncDetail 类 AsyncDetail 类(参睹清单 14)

我们还需要把这两个类添加到公共内存的基础 FrameWorkBase 类



public final class SyncDetail { private Object[] output; // output data array private Object input; // client input data, if any private int status; // 0=avail 1=busy private int nbr_que; // total queue's in function private int nbr_remaining; // remaining to be processed private int wait_time; // max wait time in seconds private Object requestor; // requestor obj to cancel wait private int[] pnp; // 0 not posted, 1 is posted public final class AsyncDetail { private Object input; // client input private QueueHeader out_agent; // agent queue, if any private int nbr_que; // nbr of queues in function private int nbr_remaining; // remaining unprocessed private int status; // 0 = available, 1 = busy private Object[] output; // output array

这些对象所驻留的数组是个链表该框架中的所有的动态数组都是链表对链表条目的访问是直接通过下标当一个线程将一个对象放进任何链表时,该线程必须传给另一个对象的全部东西就是原整数(下标)另中,在大量的使用中,扩展一个链表是很简朴的 在原来的链表上再链上一个新的链表

为同步哀求保存信息的另一个公共的地方是那些已停止的哀求的动态数组当同步哀求花费了比用户可以等待时候的更长时,哀求端的连接就终止了当一个同步哀求所花费的时候多于允许的,处理或者就不能完成哀求或者就停止了为了从停止状态恢复,就必须有一个地方来安排信息这个地方就是 StallDetail 类(参睹清单 15)

我们还需要把这个类加到公共内存的基础,即 FrameWorkBase 类



public final class StallDetail { private long entered; // time entered private int gen_name; // AsyncDetail subscript private int status; // 0 = available, 1 = busy private int failed_reason; // why it is here

我们讨论了为多组件哀求结构化环境的问题,但是这还不能作为 RMI 服务器如何知道什么组件是哀求的部分的证据

组件结构是开发者从开始就知道的信息在基本的框架中,每个函数(逻辑进程的名称)都有单一的队列在哀求署理框架,每个函数有一个队列列表与每个 函数相联的 队列的列表是组件结构这是被改变了的 FuncDetail 类(参睹清单 16)当您编码自己的系统时,而不是使用演示系统,您将根据需要为每个函数成立一个结构

通过遍历链来为哀求查找恰当的队列 传递给应用程序线程(在队列的等待列表中)足够的信息来告诉 RMI 连接线程如何指出哀求完成(通过设置 pnp[0] = 1 )以及 notifyAll() 方法的一个引用(requestor)
在队列中查找哀求的可用的应用程序线程并唤醒它 等待应用程序线程完成工作(参睹清单 12)
清单 12. RMI 连接线程等待逻辑
// Wait for the request to complete or timeout // get the monitor for this RMI object synchronized (this) { // until work finished while (pnp[0] == 0) { // wait for a post or timeout try { // max wait time is the time passed wait(time_wait); } catch (InterruptedException e) {} // When not posted if (pnp[0] == 0) { // current time time_now = System.currentTimeMillis(); // decrement wait time time_wait -= (time_now - start_time); // When no more seconds remain if (time_wait < 1) { // get out of the loop, timed out break; } else { // new start time start_time = time_now; } } } }

留意: while 循环(在 // until work finished 处)在此刻所处的位置是因为当任何应用程序线程发出 notifyAll() 时,Java 语言唤醒 所有的RMI 连接线程

逻辑进程

用队列的列表和对 client_data 的引用(参睹 清单 3)来创建一个 AsyncDetail 对象或是一个 SyncDetail 对象
通过遍历链来为快乐大本营哀求查找恰当的队列 将代表 AsyncDetail 或 SyncDetail 对象的整数下标根据劣先级放进函数的每个队列的列表中
唤醒每个队列的应用程序线程

对于同步哀求,RMI 连接线程等待直到所有的队列完成了处理然后该框架将来自所有逻辑进程的对象连成一个单一的对象数组并把对象数组返回客户机

对于自主哀求,RMI 连接线程返回客户机一个已被调度的消息处理是同步发生的当最后一个队列的应用程序完成了处理,应用程序线程将来自所有的逻辑进程的返回对象 选择的连成一个单一的对象数组,并激活一个新的逻辑进程,即署理署理应用程序可以搜检那些返回对象并为支持哀求而采取进一步的行动例如,假如所有的逻辑进程都正常完成了,那么它将发出一个提交;可则,它将发出一个回滚

看它的工作情况

我们已讨论了很多关于线程和队列的东西了,也许还有点迷惑没有什么能够象观看同步进程办理器运行更能减轻迷惑的了此刻到了把所有的东西放在一起演示的时候了假如您还没有 下载 zip 文件,那么请此刻就下载

该演示要求至少是 Java 平台 1.1 版本将文件解压缩到一个目录结构如下:

/Doc:包含一个文件: Doc.html ,该文件为所有的类和运行时过程提供了文档 /Source:包含本文的所有的源代码 /Classes:包含本文的所有的类,包括用于安全性的 policy.all 文件
FrameWorkServer

跟随启动一个单一访问的客户机(DemoClient_3,其函数是 F3)的指导,此客户机由三个队列组成(是命名为"第一次"的节)

运行时摘要

这是当你启动客户机时所发生的客户机以 FrameWorkParm 对象作为参数调用远程对象 FrameWorkServer 的 syncRequest() 方法 syncRequest() :

找到哀求的函数(命名为 F3),它包含三个队列,分别叫做 Q2Q2 和 Q3 创建一个 SyncDetail 对象,并把它的下标符号放进队列 Q1Q2 和 Q3 的等待列表中 发现 Q1 没有活泼的线程,所以初始化一个新的线程 * 发现 Q2 没有活泼的线程,所以初始化一个新的线程 * 发现 Q3 没有活泼的线程,所以初始化一个新的线程 * 等待所有的逻辑进程完成了处理 当被关照完成时,从每个逻辑进程获得返回对象,把每个对象连成一个对象数组,并把对象数组返回给客户机

留意:在标有 * 的步骤中,假如已有了活泼的线程,那么 syncRequest() 所有做的就仅是 notify() 它

2001 年 10 月 15 日

搜索等待列表来找第一个可用的哀求 为哀求获得 SyncDetail 对象 调用应用程序处理类的 doWork() 方法来执行队列的逻辑 保存来自应用程序处理类的返回对象的引用到 SyncDetail 类中 当它确定所有其它队列已完成了处理时,唤醒正在等待的 RMI 连接线程 搜索等待列表来查找第一个可用的哀求,因为没有找到,并发出一个 wait() 直到下一个 notify()

装入

当许多客户机同时的命中服务器时,令人兴奋的事情出现了!另中,没有可视化的工具的话,您就没有门径知道正在发生些什么在本包中,有两个类正是提供了这样的一个工具(即,命名为装入的指导部分)

跟随指导来运行该可视化工具, FrameWorkThreads 类

让 RMI 连接线程和应用程序线程可以彼此对话 定时哀求 无需用应用程序线程来使服务器过载就可以运行自主哀求 将署理作为任何自主哀求的一部分来运行 处理来自队列的等待列表的多哀求,从而减少了应用程序启动/停止的开消 通过保持每个事件的计数来调劣服务器 控制创建/销毁线程所带来的开消 简朴的作为应用程序线程的主题插入任何应用程序类 轻松的捕获线程或应用程序类进行调试 运行来自任何应用程序的递回哀求 使用一个管程来找到未执行的哀求,并用一个方法来处理它们 得体的封闭 RMI 服务器

然后我们把单处理环境增强为可以并行处理的哀求署理我们丰富了公共内存环境,从而:

封闭

在您处理好了服务器后,您可以用一个客户机哀求, DemoClient_Shutdown ,得体的封闭它




在本篇简洁的文章中,我们只能研究一下一个同步进程办理器的骨架我们用 框架(framework)这个词是因为这个词描述了一个骨架支持结构一些可以补充该支持的元素有:

毛病恢复:如我们上面所提到的,有时异常规的代码远比标准代码更重要对于一个定制的框架,毛病恢复取决于应用程序大多数检测取决进程的计时的不同方面捕获一个同常是很简朴的;但找出出轨的线程却很难要知道寻找什么,您必须知道应用程序是做什么的
阈值:何时实例化或激活一个应用程序线程的问题是最重要的该代码示例目前所表现的体例,框架在一个逻辑进程中实例化或激活一个新的线程的唯一的时候是当队列中没有活泼的线程或是当一个新的哀求进入一个等待列表引起溢出到下一个列表时通常在队列的负载变的比用户决定的值大时,激活另一个线程会更好些这就是 阈值处理当 RMI 连接线程把一个新的哀求放入等待列表时,线程会能够确定该队列目前的负载(根据预先确定的要求),而且会启动或激活另一个应用程序线程
Hook 和出口:开发者如何处理连接池呢?开发者如何处理消息队列中间件包呢?记住,服务器是长期的您可以添加一个启动(start-up)hook,使用它您成立用于保存那些产品的实例化的类和私有线程的单独的内存区域您可以添加一个用于得体的封闭单独的区域的封闭 (shutdown) hook
日志记录:任何曾经用后台进程(background process)的人都知道记录毛病是多么的重要发生漏洞后,还能怎么知道发生了什么呢?任何普通目的的日志就可以满足要求了此刻有可用的商业产品,而且标准的 Java 语言不久的将来也会支持记录另中请参睹开放源代码工程 Log4jNate Sammons 的关于系统日志(Syslog)的文章以及 alphaWorks Logging Toolkit for Java,所有这些都在 参考资猜中
定制对通用:这是个定制的框架您成立这样一个系统来支持应用程序的特定的鸠合当您需要支持广泛一系列的应用程序或没有时候来自己设计一个的时候,那么比较好的选择就是购买一个通用的功能完全的同步进程办理器参睹 参考资料的一般框架的一个列表 结论

本文讲了很多的内容没有人宣称成立一个后端框架是简朴的记住,Java 设计师在成立 EJB 和 GUI 容器上投入了异常多的勤奋此刻我们有什么呢?

我们将 RMI 逻辑从应用程序逻辑分离出来通过这样做,我们开发了一个应用程序队列和线程(并不局限于 RMI)的世界这个世界使我们能:

您需要一个彻底的体例来封闭服务器而不要管正在处理的哀求因为RMI 运行时包含从不结束的线程,所以结束 RMI 服务器的唯一的体例是诡计性的使用一个系统退出方法或是使用一个操作系统排除后者是最粗野的,而且要求手工的干预

从这点,RMI 服务器应用程序容器不再是空的了

它能可完成了它的承诺呢?正如您将看到的在与本文一起的 zip 文件(从 参考资料部分下载)中所包含的代码,框架表现的极好

为得到更多关于本文中提到的一些问题的信息,请浏览 JavaWorld杂志的这些文章:


关于作者



public final class FuncDetail { private String name; // Function name private long used; // times used private QueueHeader agent; // optional agent queue private int nbr_que; // number of queues in this entry private QueueHeader[] qtbl; // array of queues

从一个简朴的公共内存环境开始,到此刻我们已通过增加几个类和修改其它的类(包括增加新的类到 FrameWorkBase 类)增强了该环境来支持哀求署理图 4 是支持该框架的最终公共内存结构




对本文的评价



IBM 公司保留在 developerWorks 网站上发表的内容的著作权未经IBM公司或原始作者的书面明确许可,请勿转载假如您希望转载,请通过 提交转载哀求表单 联系我们的编纂团队



引文来源  Java RMI 服务器框架

----------------------------
一键转贴,快速捕捉生涯精彩,赢每周好礼!查看举动首页>>

自主哀求的事件流程和计时哀求的事件流程是一样的,除了自主哀求没有要求 RMI 连接线程等待完成中在唤醒了应用程序线程后,RMI 连接线程返回给客户机一个它已被调度的消息自主哀求作为成就看起来也许很简朴,但是存在一个隐藏的困难


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值