??xml version="1.0" encoding="utf-8" standalone="yes"?>精品日韩亚洲AV无码,亚洲日韩精品无码专区网址,亚洲人成自拍网站在线观看http://www.tkk7.com/sinoly/articles/97411.htmlsinolysinolyFri, 02 Feb 2007 01:00:00 GMThttp://www.tkk7.com/sinoly/articles/97411.htmlhttp://www.tkk7.com/sinoly/comments/97411.htmlhttp://www.tkk7.com/sinoly/articles/97411.html#Feedback0http://www.tkk7.com/sinoly/comments/commentRss/97411.htmlhttp://www.tkk7.com/sinoly/services/trackbacks/97411.html    作者:江南白衣Q原文出处: http://www.tkk7.com/calvin/archive/2007/01/31/96844.html Q{载请保留出处?br />
    如果说Google的搜索引擎是免费的早,Gmail们是免费的午的话,

    http://labs.google.com/papers/ 是Googlel开发h员们的一份免费的晚餐?/p>

    不过Q咋看着一桌饭菜可能不知道从哪吃vQ在自己不熟悉的领域啃英文也不是一件愉快的事情?br />

一、一份PPT与四份中文翻译?/h2>

    q好Q有一位面试google不第的老兄Q自我爆发搞了一份Google Interal的PPTQ?/p>

    http://cbcg.net/talks/googleinternals/index.htmlQ大安标点点就能跟着他匆匆过一遍google的内部架构?/p>

   然后又有崮崮p\上走9?http://sharp838.mblogger.cn)与美Z?http://my.donews.com/eraera/)Q翻译了其中最重要的四份论文:

二、Google帝国的技术基?/h2>

     Google帝国Q便建立在大U?5万台的Server上,其中大部分都?cheap x86 boxes"。而这45万台ServerQ则建立于下面的key infrastructureQ?/p>

     1.GFS(Google File System):

     GFS是适用于大规模分布式数据处理应用的分布式文件系l,是Google一切的基础Q它Z普通的g讑֤Q实C定w的设计与极高的性能。   ?

     李开复说QGoogle最厉害的技术是它的storage。我认ؓ学计机的学生都应该看看q篇文章Q再ơ感谢翻译的兄弟)。   ?br />    

     它以64MZ个Chunk(Block)Q每个Chunk臛_存在于三台机器上Q交互的单过E见:
     
    

     2.MapReduce

    MapReduce是一个分布式处理量数据集的~程模式Q让E序自动分布C个由普通机器组成的大集群上ƈ发执行。像Grep-style jobQ日志分析等都可以考虑采用它?br />
    MapReduce的run-timepȝ会解册入数据的分布l节Q跨机器集的E序执行调度Q处理机器的失效Qƈ且管理机器之间的通讯h。这L模式允许E序员可以不需要有什么ƈ发处理或者分布式pȝ的经验,可以处理超大的分布式系l得资源?/p>

     我自己接触MapReduce?a >Lucene->Nutch->Hadoop的\Uѝ?br />     Hadoop是Lucene之父Doug Cutting的又一力作Q是Java版本的分布式文gpȝ与Map/Reduce实现?br />     Hadoop的文档ƈ不详l,再看一?a >Googleq篇中文版的论文Q一切清晰很?又一ơ感谢翻译的兄弟)。   ?br />
     孟岩也有一很清晰的博客:Map Reduce - the Free Lunch is not over?

     3.BigTable

     BigTable 是Google Style的数据库Q用结构化的文件来存储数据?br />     虽然不支持关pd数据查询Q但却是建立GFS/MapReduce基础上的Q分布式存储大规模结构化数据的方案?/p>

     BigTable是一个稀疏的Q多l的Q排序的MapQ每个Cellp关键字,列关键字和时间戳三维定位QCell的内Ҏ一个不解释的字W串?br />     比如下表存储每个|站的内容与被其他网站的反向q接的文本?br />     反向的URL com.cnn.www(www.cnn.com)是行的关键字Qcontents列存储网内容,每个内容有一个时间戳Q因为有两个反向q接Q所以archor列族有两?anchor:cnnsi.com和anchhor:my.look.caQ列族的概念Q得表可以横向扩展Qarchor的列数ƈ不固定?/p>

   

    Zq发dQ热区,HA{考虑QBigTable当然不会存在逗号分割的文本文件中Q,是存储在一U叫SSTable的数据库l构上,q有BMDiff和Zippy两种不同侧重点的压羃法?/p>

4.Sawzall

    Sawzall是一U徏立在MapReduce基础上的领域语言Q可以被认ؓ是分布式的awk。它的程序控制结?if,while)与C语言无异Q但它的领域语言语义使它完成相同功能的代码与MapReduce的C++代码相比化了10倍不止?/p>

1    proto "cvsstat.proto"
2    submits: table sum[hour: int] of count: int;
3    log: ChangelistLog = input;
4    hour: int = hourof(log.time)
5    emit submits[hour] <- 1;

    
     天书?慢慢看吧?/p>

     我们q次是统计在每天24时里CVS提交的次数?br />     首先它的变量定义cMPascal  (i:int=0; 卛_义变量iQ类型ؓintQ初始gؓ0)

     1:引入cvsstat.proto协议描述Q作用见后?br />     2:定义int数组submits 存放l计l果Q用hour作下标?br />     3.循环的将文g输入转换为ChangelistLog cdQ存储在log变量里,cd及{换方法在前面的cvsstat.proto描述?br />     4.取出changlog中的提交旉log.time的hour倹{?br />     5.emit聚合Q在sumitsl果数组里,hour的提交数?Q然后自动@环下一个输入?

     居然L了,其中1?步是准备与定义,3?步是MapQ第5步是Reduce?br />

? 结Q?/h2>

  本文只是单的介绍Google的技术概貌,大家知道以后除了可作谈资外没有Q何作用,我们真正要学习的骨血Q是论文里如何解决高q发Q高可靠性等的设计思\和细?....



sinoly 2007-02-02 09:00 发表评论
]]>JAVA性能优化Q通用(转)http://www.tkk7.com/sinoly/articles/97408.htmlsinolysinolyFri, 02 Feb 2007 00:59:00 GMThttp://www.tkk7.com/sinoly/articles/97408.htmlhttp://www.tkk7.com/sinoly/comments/97408.htmlhttp://www.tkk7.com/sinoly/articles/97408.html#Feedback0http://www.tkk7.com/sinoly/comments/commentRss/97408.htmlhttp://www.tkk7.com/sinoly/services/trackbacks/97408.html“通用”讨论的问题适合于大多数Java应用?

  1.1 不用new关键词创建类的实?

  用new关键词创建类的实例时Q构造函数链中的所有构造函数都会被自动调用。但如果一个对象实CCloneable接口Q我们可以调用它的clone()Ҏ。clone()Ҏ不会调用McL造函数?

  在用设计模?Design Pattern)的场合,如果用Factory模式创徏对象Q则改用clone()Ҏ创徏新的对象实例非常单。例如,下面是Factory模式的一个典型实玎ͼ

public static Credit getNewCredit()
  {

   return new Credit();

  }

 


  改进后的代码使用clone()ҎQ如下所C:

private static Credit BaseCredit = new Credit();
  public static Credit getNewCredit()

  {

   return (Credit) BaseCredit.clone();

  }


   上面的思\对于数组处理同样很有用?


  1.2 使用非阻塞I/O

  版本较低的JDK不支持非dI/O API。ؓ避免I/OdQ一些应用采用了创徏大量U程的办?在较好的情况下,会用一个缓冲池)。这U技术可以在许多必须支持q发I/O的应用中见刎ͼ如Web服务器、报价和拍卖应用{。然而,创徏JavaU程需要相当可观的开销?

  JDK 1.4引入了非d的I/O?java.nio)。如果应用要求用版本较早的JDKQ在q里有一个支持非dI/O的Y件包?

  1.3 慎用异常

  异常Ҏ能不利。抛出异帔R先要创徏一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本?(Native)ҎQfillInStackTrace()Ҏ查堆栈,攉调用跟踪信息。只要有异常被抛出,VM必调整调用堆栈,因ؓ在处理过E中创徏了一个新的对象?

  异常只能用于错误处理Q不应该用来控制E序程?

  1.4 不要重复初始化变?

  默认情况下,调用cȝ构造函数时Q?Java会把变量初始化成定的|所有的对象被设|成nullQ整数变?byte、short、int、long)讄?Qfloat?double变量讄?.0Q逻辑D|成false。当一个类从另一个类zӞq一点尤其应该注意,因ؓ用new关键词创Z个对象时Q构造函数链中的所有构造函数都会被自动调用?

  1.5 量指定cȝfinal修饰W?

  带有final修饰W的cL不可z的。在Java核心API中,有许多应用final的例子,例如java.lang.String。ؓStringcL定final防止了h们覆盖length()Ҏ?

  另外Q如果指定一个类为finalQ则该类所有的Ҏ都是final。Java~译器会LZ内联(inline)所有的finalҎ(q和具体的编译器实现有关)。此举能够性能q_提高50%?

  1.6 量使用局部变?

  调用Ҏ时传递的参数以及在调用中创徏的时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等Q都在堆(Heap)中创建,速度较慢。另外,依赖于具体的~译?JVMQ局部变量还可能得到q一步优化。请参见《尽可能使用堆栈变量》?

  1.7 乘法和除?

  考虑下面的代码:

  for (val = 0; val < 100000; val +=5)

  {

   alterX = val * 8;

   myResult = val * 2;

  }

  用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码Q?

  for (val = 0; val < 100000; val += 5)

  {

   alterX = val << 3;

   myResult = val << 1;

  }

 

  修改后的代码不再做乘?的操作,而是改用{h的左U?位操作,每左U?位相当于乘以2。相应地Q右U?位操作相当于除以2。值得一提的是,虽然UM操作速度快,但可能代码比较难于理解Q所以最好加上一些注释?/div>

sinoly 2007-02-02 08:59 发表评论
]]>
Java中实现图片裁??http://www.tkk7.com/sinoly/articles/97406.htmlsinolysinolyFri, 02 Feb 2007 00:58:00 GMThttp://www.tkk7.com/sinoly/articles/97406.htmlhttp://www.tkk7.com/sinoly/comments/97406.htmlhttp://www.tkk7.com/sinoly/articles/97406.html#Feedback0http://www.tkk7.com/sinoly/comments/commentRss/97406.htmlhttp://www.tkk7.com/sinoly/services/trackbacks/97406.html Java 如何截取囄  

Author      :      zhyiwww

E-Mail       :      zhyiwww@163.com

Date           :      2007-1-30

转蝲h明出?/span> www.tkk7.com/zhyiwww

                                (copyright by @ zhangyi)  

下面是我的一D代码,实现如何截取囄的:

         // 囄?/span>

       private static final String SRC_FILE="org//zy//demo//jdk//base//image//car1.jpg";

      // 目标囄

       private static final String DEST_FILE="c://a.jpg";

 

/**

        * d囑փ文g

        * ?/span> ImageReader

        * @param imgPath

        * @throws IOException

        */

       public void readUsingImageReader(String imgPath) throws IOException{

             

              // 取得囄d?/span>

              Iterator readers = ImageIO.getImageReadersByFormatName("jpg");

              System.out.println(readers);

              ImageReader reader = (ImageReader)readers.next();

              System.out.println(reader);

             

              // 取得囄d?/span>

              InputStream source=this.parseImagePath(ImageDemo.SRC_FILE);

              ImageInputStream iis = ImageIO.createImageInputStream(source);

             

              reader.setInput(iis, true);

             

              // 囄参数

             

              ImageReadParam param = reader.getDefaultReadParam();

              int imageIndex = 0;

              int half_width = reader.getWidth(imageIndex)/2;

              int half_height = reader.getHeight(imageIndex)/2;

//            Rectangle rect = new Rectangle(60, 60, half_width, half_height);

              Rectangle rect = new Rectangle(60, 60, 100, 100);

             

              param.setSourceRegion(rect);

      

              BufferedImage bi = reader.read(0,param);             

             

              ImageIO.write(bi, "jpg", this.initDestFile());          

             

       }

 

我的源图片是Q?/span>

<!--[if !vml]--> img0.jpg
<!--[endif]-->

 

上面的程序运行后截得的图片如下:

<!--[if !vml]--> img1.jpg
<!--[endif]-->

 



sinoly 2007-02-02 08:58 发表评论
]]>Java RMI TutorialQ收录)http://www.tkk7.com/sinoly/articles/97169.htmlsinolysinolyThu, 01 Feb 2007 03:29:00 GMThttp://www.tkk7.com/sinoly/articles/97169.htmlhttp://www.tkk7.com/sinoly/comments/97169.htmlhttp://www.tkk7.com/sinoly/articles/97169.html#Feedback0http://www.tkk7.com/sinoly/comments/commentRss/97169.htmlhttp://www.tkk7.com/sinoly/services/trackbacks/97169.htmlq程Ҏ调用入门指南QJava RMI TutorialQ?/h4>

Java R MI Tutorial

q程Ҏ调用入门指南

Copyright © 2005 Stephen Suen. All rights reserved.

Java q程Ҏ调用QRemote Method Invocation, RMIQ得运行在一?Java 虚拟机(Java Virtual Machine, JVMQ的对象可以调用q行另一?JVM 之上的其他对象的ҎQ从而提供了E序间进行远E通讯的途径。RMI ?J2EE 的很多分布式技术的基础Q比?RMI-IIOP 乃至 EJB。本文是 RMI 的一个入门指南,目的在于帮助读者快速徏立对 Java RMI 的一个感性认识,以便q行更深层次的学习。事实上Q如果你了解 RMI 的目的在于更好的理解和学?EJBQ那么本文就再合适不q了。通过本文所了解?RMI 的知识和技巧,应该_服务于这个目的了?

 

1. ?/font>

我们知道q程q程调用QRemote Procedure Call, RPCQ可以用于一个进E调用另一个进E(很可能在另一个远E主ZQ中?span>q程Q从而提供了q程的分布能力。Java ?RMI 则在 RPC 的基上向前又q进了一步,x供分布式 对象间的通讯Q允许我们获得在q程q程中的对象Q称E对象)的引用(UCؓq程引用Q,q而通过引用调用q程对象的方法,好像该对象是与你的客户端代码同栯行在本地q程中一栗RMI 使用了术?Ҏ"QMethodQ强调了q种q步Q即在分布式基础上,充分支持面向对象的特性?

RMI q不?Java 中支持远E方法调用的唯一选择。在 RMI 基础上发展而来?RMI-IIOPQJava Remote Method Invocation over the Internet Inter-ORB ProtocolQ,不但l承?RMI 的大部分优点Qƈ且可以兼容于 CORBA。J2EE ?EJB 都要求?RMI-IIOP 而不?RMI。尽如此,理解 RMI 大大有助于 RMI-IIOP 的理解。所以,即便你的兴趣?RMI-IIOP 或?EJBQ相信本文也会对你很有帮助。另外,如果你现在就?API 感兴,那么可以告诉你,RMI 使用 java.rmi 包,?RMI-IIOP 则既使用 java.rmi 也用扩展的 javax.rmi 包?

本文的随后内容将仅针?Java RMI?/p>

2. 分布式对?/font>

在学?RMI 之前Q我们需要了解一些基知识。首先需要了解所谓的分布式对象(Distributed ObjectQ。分布式对象是指一个对象可以被q程pȝ所调用。对?Java 而言Q即对象不仅可以被同一虚拟Z的其他客L序(ClientQ调用,也可以被q行于其他虚拟机中的客户E序调用Q甚臛_以通过|络被其他远E主Z上的客户E序调用?

下面的图C明了客户E序是如何调用分布式对象的:

从图上我们可以看刎ͼ分布式对象被调用的过E是q样的:

  1. 客户E序调用一个被UCؓ Stub Q有时译作存根,Z不生歧义,本文用其英文形式Q的客户端代理对象。该代理对象负责对客L隐藏|络通讯的细节。Stub 知道如何通过|络套接字(SocketQ发送调用,包括如何调用参数{换ؓ适当的Ş式以便传输等?

  2. Stub 通过|络调用传递到服务器端Q也是分布对象一端的一个被UCؓ Skeleton 的代理对象。同P该代理对象负责对分布式对象隐藏网l通讯的细节。Skeleton 知道如何从网l套接字QSocketQ中接受调用Q包括如何将调用参数从网l传输Ş式{换ؓ Java 形式{?

  3. Skeleton 调用传递给分布式对象。分布式对象执行相应的调用,之后返回g递给 SkeletonQ进而传递到 StubQ最l返回给客户E序?/p>

q个场景Z一个基本的法则Q即行ؓ的定义和行ؓ的具体实现相分离。如图所C,客户端代理对?Stub 和分布式对象都实C相同的接口,该接口称E接口(Remote InterfaceQ。正是该接口定义了行为,而分布式对象本n则提供具体的实现。对?Java RMI 而言Q我们用接口Q?font color="red">interfaceQ定义行为,用类Q?font color="red">classQ定义实现?

3. RMI 架构

RMI 的底层架构由三层构成Q?

  • 首先?Stub/Skeleton 层。该层提供了客户E序和服务程序彼此交互的接口?

  • 然后是远E引用(Remote ReferenceQ层。这一层相当于在其之上?Stub/Skeleton 层和在其之下的传输协议层之前的中间gQ负责处理远E对象引用的创徏和管理?

  • 最后是传输协议QTransport ProtocolQ?层。该层提供了数据协议Q用以通过U\传输客户E序和远E对象间的请求和应答?/p>

q些层之间的交互可以参照下面的示意图Q?

和其它分布式对象机制一PJava RMI 的客L序用客L?Stub 向远E对象请求方法调用;服务器对象则通过服务器端?Skeleton 接受h。我们深入进去,来看看其中的一些细节?

注意: 事实上,?Java 1.2 之后QRMI 不再需?Skeleton 对象Q而是通过 Java 的反机ӞReflectionQ来完成Ҏ务器端的q程对象的调用。ؓ了便于说明问题,本文以下内容仍然Z Skeleton 来讲解?/p>

当客L序调?Stub ӞStub 负责方法的参数转换为序列化QSerializedQŞ式,我们使用一个特D的术语Q即~列QMarshalQ来指代q个q程。编列的目的是将q些参数转换为可UL的Ş式,从而可以通过|络传输到远E的服务对象一端。不q的是,q个q程没有惌中那么简单。这里我们首先要理解一个经典的问题Q即Ҏ调用Ӟ参数I竟是传D是传引用呢?对于 Java RMI 来说Q存在四U情况,我们分别加以说明?

  • 对于基本的原始类型(整型Q字W型{等Q,被自动的序列化Q以传值的方式~列?

  • 对于 Java 的对象,如果该对象是可序列化的(实现?java.io.Serializable 接口Q,则通过 Java 序列化机制自动地加以序列化,以传值的方式~列。对象之中包含的原始cd以及所有被该对象引用,且没有声明ؓ transient 的对象也自动的序列化。当Ӟq些被引用的对象也必L可序列化的?

  • l大多数内徏?Java 对象都是可序列化的?对于不可序列化的 Java 对象Q?font color="red">java.io.File 最典型Q,或者对象中包含对不可序列化Q且没有声明?transient 的其它对象的引用。则~列q程向客户E序抛出异常Q而宣告失败?

  • 客户E序可以调用q程对象Q没有理q止调用参数本w也是远E对象(实现?java.rmi.Remote 接口的类的实例)。此ӞRMI 采用一U?span>模拟?/i>传引用方式(当然不是传统意义的传引用Q因为本地对内存的引用到了远E变得毫无意义)Q而不是将参数直接~列复制到远E。这U情况下Q交互的双方发生的戏剧性变化值得我们注意。参数是q程对象Q意味着该参数对象可以远E调用。当客户E序指定q程对象作ؓ参数调用服务器端q程对象的方法时QRMI 的运行时机制向服务器端的远E对象发送作为参数的q程对象的一?Stub 对象。这h务器端的q程对象可以回调(CallbackQ这?Stub 对象的方法,q而调用在客户端的q程对象的对应方法。通过q种ҎQ服务器端的q程对象可以修改作为参数的客户端远E对象的内部状态,q正是传l意义的传引用所具备的特性。是不是有点晕?q里的关键是要明白,在分布式环境中,所谓服务器和客L都是相对的。被h的一方就是服务器Q而发求的一方就是客L?

在调用参数的~列q程成功后,客户端的q程引用层从 Stub 那里获得了编列后的参C及对服务器端q程对象的远E引用(参见 java.rmi.server.RemoteRef APIQ。该层负责将客户E序的请求依据底层的 RMI 数据传输协议转换Z输层h。在 RMI 中,有多U的可能的传输机Ӟ比如点对点(Point-to-PointQ以及广播(MulticastQ等。不q,在当前的 JMI 版本中只支持点对点协议,卌E引用层生成唯一的传输层hQ发往指定的唯一q程对象Q参?java.rmi.server.UnicastRemoteObject APIQ?

在服务器端,服务器端的远E引用层接收传输层请求,q将其{换ؓ对远E对象的服务器端代理对象 Skeleton 的调用。Skeleton 对象负责请求{换ؓ对实际的q程对象的方法调用。这是通过与编列过E相对的反编列(UnmarshalQ过E实现的。所有序列化的参数被转换?Java 形式Q其中作为参数的q程对象Q实际上发送的是远E引用)被{换ؓ服务器端本地?Stub 对象?

如果Ҏ调用有返回值或者抛出异常,?Skeleton 负责~列q回值或者异常,通过服务器端的远E引用层Q经传输层传递给客户端;相应圎ͼ客户端的q程引用层和 Stub 负责反编列ƈ最l将l果q回l客L序?

整个q程中,可能最让hqh的是q程引用层。这里只要明白,本地?Stub 对象是如何生的Q就不难理解q程引用的意义所在了。远E引用中包含了其所指向的远E对象的信息Q该q程引用用于构造作为本C理对象的 Stub 对象。构造后QStub 对象内部维护该q程引用。真正在|络上传输的实际上就是这个远E引用,而不?Stub 对象?/p>

4. RMI 对象服务

?RMI 的基本架构之上,RMI 提供服务与分布式应用E序的一些对象服务,包括对象的命?注册QNaming/RegistryQ服务,q程对象Ȁz(ActivationQ服务以及分布式垃圾攉QDistributed Garbage Collection, DGCQ。作为入门指南,本文指介绍其中的命?注册服务Q因为它是实?RMI 所必备的。其它内容请读者自行参考其它更加深入的资料?

在前一节中Q如果你喜欢刨根问底Q可能已l注意到Q客L要调用远E对象,是通过其代理对?Stub 完成的,那么 Stub 最早是从哪里得来的呢?RMI 的命?注册服务正是解决q一问题的。当服务器端惛_客户端提供基?RMI 的服务时Q它需要将一个或多个q程对象注册到本地的 RMI 注册表中Q参?font color="red">java.rmi.registry.Registry APIQ。每个对象在注册旉被指定一个将来用于客L序引用该对象的名U。客L序通过命名服务Q参?java.rmi.Naming APIQ,指定cM URL 的对象名U就可以获得指向q程对象的远E引用。在 Naming 中的 lookup() Ҏ扑ֈq程对象所在的L后,它将索该L上的 RMI 注册表,q请求所需的远E对象。如果注册表发现被请求的q程对象Q它生成一个对该远E对象的q程引用Qƈ其q回l客LQ客L则基于远E引用生成相应的 Stub 对象Qƈ引用传递给调用者。之后,双方可以按照我们前面讲q的方式q行交互了?

注意: RMI 命名服务提供?Naming cdƈ不是你的唯一选择。RMI 的注册表可以与其他命名服务绑定,比如 JNDIQ这样你可以通过 JNDI 来访?RMI 的注册表了?/p>

5. 实战 RMI

理论M开实践Q理?RMI 的最好办法就是通过例子。开?RMI 的分布式对象的大体过E包括如下几步:

  1. 定义q程接口。这一步是通过扩展 java.rmi.Remote 接口Qƈ定义所需的业务方法实现的?

  2. 定义q程接口的实现类。即实现上一步所定义的接口,l出业务Ҏ的具体实现逻辑?

  3. ~译q程接口和实现类Qƈ通过 RMI ~译?rmic Z实现cȝ成所需?Stub ?Skeleton cR?/p>

RMI 中各个组件之间的关系如下面这个示意图所C:

回忆我们上一节所讲的QStub ?Skeleton 负责代理客户和服务器之间的通讯。但我们q不需要自q成它们,相反QRMI 的编译器 rmic 可以帮我们基于远E接口和实现cȝ成这些类。当客户端对象通过命名服务向服务器端的 RMI 注册表请求远E对象时QRMI 自动构造对应远E对象的 Skeleton 实例对象Qƈ通过 Skeleton 对象远E引用返回给客户端。在客户端,该远E引用将用于构?Stub cȝ实例对象。之后,Stub 对象?Skeleton 对象可以代理客户对象和q程对象之间的交互了?

我们的例子展C一个简单的应用场景。服务器端部|了一个计引擎,负责接受来自客户端的计算dQ在服务器端执行计算dQƈ结果返回给客户端。客L发送ƈ调用计算引擎的计Q务实际上是计指定精度的 π 倹{?

重要: 本文的例子改~自 The Java?Tutorial Trail:RMI。所有权利属于相应的所有h?/p>

6. 定义q程接口

定义q程接口与非分布式应用中定义接口的方法没有太多的区别。只要遵守下面两个要求:

  • q程接口必须直接或者间接的扩展?java.rmi.Remote 接口。远E接口还可以在扩展该接口的基上,同时扩展其它接口Q只要被扩展的接口的所有方法与q程接口的所有方法一h下一个要求?

  • 在远E接口或者其接口(Super-interfaceQ中声明的方法必L下列对q程Ҏ的要求:

    • q程Ҏ必须声明抛出 java.rmi.RemoteException 异常Q或者该异常的超c(SuperclassQ,比如 java.io.IOException 或?java.lang.Exception 异常。在此基上,q程Ҏ可以声明抛出应用特定的其它异常?

    • 在远E方法声明中Q作为参数或者返回值的q程对象Q或者包含在其它非远E对象中的远E对象,必须声明为其对应的远E接口,而不是实际的实现cR?/p>

注意: ?Java 1.2 之前Q上面关于抛出异常的要求更严|卛_L?java.rmi.RemoteExcptionQ不允许cM java.io.IOException q样的超cR现在之所以放宽了q一要求Q是希望可以使定义既可以用于q程对象Q也可以用于本地对象的接口变得容易一些(x EJB 中的本地接口和远E接口)。当Ӟqƈ没有佉K题好多少Q你q是必须声明异常。不q,一U观点认不是问题Q强制声明异常可以开发h员保持清醒的头脑Q因E对象和本地对象在调用时传参的语意是不同的。本地对象是传引用,而远E对象主要是传|q意呛_参数内部状态的修改产生的结果是不同的?

对于W一个要求,java.rmi.Remote 接口实际上没有Q何方法,而只是用作标记接口。RMI 的运行环境依赖该接口判断对象是否是远E对象。第二个要求则是因ؓ分布式应用可能发生Q何问题,比如|络问题{等?

?1 列出了我们的q程接口定义。该接口只有一个方法:executeTask() 用以执行指定的计Q务,q返回相应的l果。注意,我们用后~ Remote 表明接口是远E接口?

?1. ComputeEngineRemote q程接口

package rmitutorial;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ComputeEngineRemote extends Remote {
    public Object executeTask(Task task) throws RemoteException;    
}

?2 列出了计Q务接口的定义。该接口也只有一个方法:execute() 用以执行实际的计逻辑Qƈq回l果。注意,该接口不是远E接口,所以没有扩?java.rmi.Remote 接口Q其Ҏ也不必抛?java.rmi.RemoteException 异常。但是,因ؓ它将用作q程Ҏ的参敎ͼ所以扩展了 java.io.Serializable 接口?

?2. Task 接口

package rmitutorial;

import java.io.Serializable;

public interface Task extends Serializable {
    Object execute();
}

7. 实现q程接口

接下来,我们实现前面定义的q程接口?a>?3l出了实现的源代码?

?3. ComputeEngine 实现

package rmitutorial;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class ComputeEngine extends UnicastRemoteObject 
        implements ComputeEngineRemote {
    
    public ComputeEngine() throws RemoteException {
        super();
    }
    
    public Object executeTask(Task task) throws RemoteException {
        return task.execute();
    }
}

c?ComputeEngine 实现了之前定义的q程接口Q同时承自 java.rmi.server.UnicastRemoteObject 类?font color="red">UnicastRemoteObject cL一个便LQ它实现了我们前面所讲的Z TCP/IP 的点对点通讯机制。远E对象都必须从该cL展(除非你想自己实现几乎所?UnicastRemoteObject 的方法)。在我们的实现类的构造函CQ调用了类的构造函敎ͼ当然Q即使你不显式的调用q个构徏函数Q它也一样会被调用。这里这样做Q只是ؓ了突出强调这U调用而已Q。该构造函数的最重要的意义就是调?UnicastRemoteObjectexportObject() Ҏ。导出(ExportQ对象是指ɘq程对象准备qAQ可以接受进来的调用的过E。而这个过E的最重要内容是建立服务器套接字Q监听特定的端口Q等待客L的调用请求?/p>

8. 引导E序

Z让客L序可以找到我们的q程对象Q就需要将我们的远E对象注册到 RMI 的注册表。这个过E有时被UCؓ"引导"q程QBootstrapQ。我们将为此~写一个独立的引导E序负责创徏和注册远E对象?a>?4 l出了引导程序的源代码?

?4. 引导E序

package rmitutorial;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class Bootstrap {
    
    public static void main(String[] args) throws Exception {
        String name = "ComputeEngine";
        
        ComputeEngine engine = new ComputeEngine();
        System.out.println("ComputerEngine exported");
        
        Naming.rebind(name, engine);
        System.out.println("ComputeEngine bound");
    }
}

可以看到Q我们首先创Z一个远E对象(同时导出了该对象Q,之后该对象l定?RMI 注册表中?font color="red">Naming ?rebind() Ҏ接受一?URL 形式的名字作l定之用。其完整格式如下Q?a>

protocol://host:port/object

其中Q协议(ProtocolQ默认ؓ rmiQ主机名默认?localhostQ端口默认ؓ 1099。注意,JDK 中提供的默认 Naming 实现只支?rmi 协议。在我们的引导程序里面只l出了对象绑定的名字Q而其它部分均使用~省倹{?/p>

9. 客户端程?/font>

?5 l出了我们的客户端程序。该E序接受两个参数Q分别是q程对象所在的L地址和希望获得的 π 值的_ֺ?

?5. Client.java

package rmitutorial;

import java.math.BigDecimal;
import java.rmi.Naming;

public class Client {
    public static void main(String args[]) throws Exception {
            String name = "rmi://" + args[0] + "/ComputeEngine";
            ComputeEngineRemote engineRemote = 
                    (ComputeEngineRemote)Naming.lookup(name);
            Pi task = new Pi(Integer.parseInt(args[1]));
            BigDecimal pi = (BigDecimal)(engineRemote.executeTask(task));
            System.out.println(pi);
    }
}

?6. Pi.java

package rmitutorial;

import java.math.*;

public class Pi implements Task {
    
    private static final BigDecimal ZERO =
            BigDecimal.valueOf(0);
    private static final BigDecimal  ONE =
            BigDecimal.valueOf(1);
    private static final BigDecimal FOUR =
            BigDecimal.valueOf(4);
    
    private static final int roundingMode =
            BigDecimal.ROUND_HALF_EVEN;
    
    private int digits;
    
    public Pi(int digits) {
        this.digits = digits;
    }
    
    public Object execute() {
        return computePi(digits);
    }
    
    public static BigDecimal computePi(int digits) {
        int scale = digits + 5;
        BigDecimal arctan1_5 = arctan(5, scale);
        BigDecimal arctan1_239 = arctan(239, scale);
        BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
                arctan1_239).multiply(FOUR);
        return pi.setScale(digits,
                BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal arctan(int inverseX,
            int scale) {
        BigDecimal result, numer, term;
        BigDecimal invX = BigDecimal.valueOf(inverseX);
        BigDecimal invX2 =
                BigDecimal.valueOf(inverseX * inverseX);
        
        numer = ONE.divide(invX, scale, roundingMode);
        
        result = numer;
        int i = 1;
        do {
            numer =
                    numer.divide(invX2, scale, roundingMode);
            int denom = 2 * i + 1;
            term =
                    numer.divide(BigDecimal.valueOf(denom),
                    scale, roundingMode);
            if ((i % 2) != 0) {
                result = result.subtract(term);
            } else {
                result = result.add(term);
            }
            i++;
        } while (term.compareTo(ZERO) != 0);
        return result;
    }
}

10. ~译CZE序

~译我们的示例程序和~译其它非分布式的应用没什么区别。只是编译之后,需要?RMI ~译器,?rmic 生成所需 Stub ?Skeleton 实现。?rmic 的方式是我们的q程对象的实现类Q不是远E接口)的全cd作ؓ参数来运?rmic 命o。参考下面的CZQ?a>

E:\classes\rmic rmitutorial.ComputeEngine

~译之后生?rmitutorial.ComputeEngine_Skel ?rmitutorial.ComputeEngine_Stub 两个cR?/p>

11. q行CZE序

q程对象的引用通常是通过 RMI 的注册表服务以及 java.rmi.Naming 接口获得的。远E对象需要导出(注册Q相应的q程引用到注册表服务Q之后注册表服务可以监听ƈ服务于客L对远E对象引用的h。标准的 Sun Java SDK 提供了一个简单的 RMI 注册表服务程序,?rmiregistry 用于监听特定的端口,{待q程对象的注册,以及客户端对q些q程对象引用的检索请求?

在运行我们的CZE序之前Q首先要启动 RMI 的注册表服务。这个过E很单,只要直接q行 rmiregistry 命o卛_。缺省的情况下,该服务将监听 1099 端口。如果需要指定其它的监听端口Q可以在命o行指定希望监听的端口Q如果你指定了其它端口,需要修改示例程序以适应环境Q。如果希望该E序在后台运行,?Unix 上可以以如下方式q行Q当Ӟ可以~省端口参数Q:

$ rmiregistry 1099 &

?Windows 操作pȝ中可以这栯行:

C:\> start rmiregistry 1099

我们?rmitutorial.Bootstrap cd用于启动q程对象Qƈ其l定?RMI 注册表中。运行该cdQ远E对象也进入监听状态,{待来自客户端的Ҏ调用h?a>

$ java rmitutorial.Bootstrap
ComputeEngine exported
ComputeEngine bound

启动q程对象后,打开另一个命令行H口Q运行客L。命令行的第一个参Cؓ RMI 注册表的地址Q第二个参数为期望的 π 值精度。参考下面的CZQ?a>

$ java rmitutorial.Client localhost 50
3.14159265358979323846264338327950288419716939937511

12. 其它信息

在演C示例程序时Q我们实际上是在同一L上运行的服务器和客户端,q且无论是服务器和客L所需的类都在相同的类路径上,可以同时被服务器和客L所讉K。这忽略?Java RMI 的一个重要细节,卛_态类装蝲。因?RMI 的特性(包括其它几个Ҏ)q不适用?J2EE ?RMI-IIOP ?EJB 技术,所以,本文不作详l介l,误者自行参考本文给出的参考资料。不q,Z让好奇的读者不至于q分失望Q这里简单介l一下动态类装蝲的基本思想?

RMI q行时系l采用动态类装蝲机制来装载分布式应用所需的类。如果你可以直接讉K应用所涉及的所有包括服务器端客L在内的主机,q且可以把分布式应用所需的所有类都安装在每个L?CLASSPATH 中(上面的示例就是极端情况,所有的东西都在本地LQ,那么你完全不必关?RMI c装载的l节。显Ӟ既然是分布式应用Q情况往往正相反。对?RMI 应用Q客L需要装载客L自n所需的类Q将要调用的q程对象的远E接口类以及对应?Stub c;服务器端则要装蝲q程对象的实现类以及对应?Skeleton c(Java 1.2 之后不需?Skeleton c)。RMI 在处理远E调用涉及的q程引用Q参C及返回值时Q可以将一个指定的 URL ~码到流中。交互的另一端可以通过 ?URL 获得处理q些对象所需的类文g。这一点类g Applet 中的 CODEBASE 的概念,交互的两端通过 HTTP 服务器发布各自控制的c,允许交互的另一端动态下载这些类。以我们的示例ؓ例,客户端不必部|?ComputeEngine_Stub 的类文gQ而可以通过服务器端?HTTP 服务器获得类文g。同P服务器端也不需要客L实现的定制Q?Pi 的类文g?

注意Q这U动态类装蝲需要交互的两端加蝲定制的安全管理器Q参?java.rmi.RMISecurityManager APIQ,以及对应的策略文件?/p>

13. 参考资?/font>

  • The Java?Tutorial Trail:RMI

  • David Flanagan, Jim Farley, William Crawford and Kris Magnusson, 1999, ISBN 1-56592-483-5E, O'Reilly, Java?Enterprise in a Nutshell

  • Ed Roman, Scott Ambler and Tyler Jewell 2002, ISBN 0-471-41711-4, John Wiley &Sons, Inc., Matering Enterprise JavaBeans?/i> , Second Edition



sinoly 2007-02-01 11:29 发表评论
]]> վ֩ģ壺 þþƷѹۿͬ| 99aƷ| йavƬ| ɫ͵͵ݺۺ| һAëƬѹۿþþƷ | һ2342021ѹۿ һ234޿Ƶ | 鶹һƷһAVһ| ͽXXXX| avƬ߹ۿ| AV鶹| Ƶ| ŷղ߹ۿ| 91۲ѹ| ޸Ƶһ| ƷþþþþþþӰԺ| һд| պƵ| 붯xxxxx| ˳ɫ77777| Ƶۿ| 츾avҹ벻 | Ʒ| þ޾ƷƷ| 2015պƵ| ˳ɵӰ߹ۿ| Ʒv߹ۿ| ŮaëƬѹۿ| vaþþþúݺ| þþƷһ99| Ƶ| ޳aƬ߹ۿ| ѿAAƬƵ| ޹Ʒһ| һ| òѸ| ɫһ| Ļ߹ۿ| þù߳׽ѹۿ| ޴Ů߹ۿ| þþ޾Ʒ| 뾫ƷӰ|