转;http://blog.csdn.net/jackliang55/article/details/7580563

我也承认,RPC的名声大噪之时是在2003年,那一个“冲击波”病毒(Blaster Worm virus)袭卷全球的一年。而“冲击波”正是用着RPC这把刀来敲开了远程电脑的大门。当然RPC 有更多正面的应用,比如NFS、Web Service等等。

一、RPC的介绍

  什么是RPC?Remote Procedure Call,远程过程调用。也就是说,调用过程代码并不是在调用者本地运行,而是要实现调用者与被调用者二地之间的连接与通信。比较严格的定义是:Remote procedure call (RPC) is aprotocol that allows a computer program running on one computer to cause a subroutine on another computer to be executed without the programmer explicitly coding the details for this interaction. When the software in question is written using object-oriented principles, RPC may be referred to as remote invocation orremote method invocation. 这样一讲,容易联想到C/S模式的程序设计,我想是对的。RPC的基本通信模型是基于Client/Server进程间相互通信模型的一种同步通信形式;它对Client提供了远程服务的过程抽象,其底层消息传递操作对Client是透明的。在RPC中,Client即是请求服务的调用者(Caller),而Server则是执行Client的请求而被调用的程序 (Callee)。

  下图是RPC调用协议图:

  
   有很多文章对这张经典的图作了很好的描述,归纳讲即是:首先是建立RPC服务,约定底层的RPC传输通道(UDP或是TCP)。客户端的调用参数根据传输前所提供的目的地址及RPC 上层应用程序号,通过底层的RPC传输通道转至相应的服务器,即RPC Application Porgramme Server。客户端随即处于等待状态,以服务器等待应答或Time Out超时信号。当服务器端获得了请求消息,会根据注册RPC时告诉RPC系统的程序入口地址执行相应的操作,并将结果返回至客户端。当一次RPC调用结束后,相应线程发送相应的信号,客户端程序便继续运行。有三个要素来标识唯一的远程过程:程序号、版本号、过程号。其中,程序号是用来区别一组相关的并且具有唯一过程号的远程过程;一个程序可以有一个或几个不同的版本;而每个版本的程序都包含一系列能被远程调用的过程。(这句比较拗口难读的话,一会儿用代码来解释)同一个版本可以包含有许多可供远程调用的过程,每个过程则有其唯一标示的过程号。通过版本的引入,使得不同版本下的 RPC能同时提供服务。

  至于更深入的RPC知识,就超出了文本讨论的东西了,在这里我们主要还是来谈以Java实现的问题。

二、RPC应用开发步骤

  由我们上面对于RPC调用协议图的讲解看来,RPC的开发一般涉及三方面:
  1.定义客户端、服务器端的通信协议。此处的通信协议是指定义服务过程的名称、调用参数的数据类型、返回参数的数据类型、底层传输类型(UDP/TCP)等等。

  2.开发客户端程序。

  3.开发服务器端程序。

对于RPC通信协议的生成,最简单的方法是利用协议编译工具。常用的是rpcgen,不过这是一个用于生成实现RPC协议的C程序的生成器。要使用Java来实现的话,我们需要使用另外的生成器,即是下面要讲的Remotetea。

三、工具介绍

  说起Remotetea可能有很多朋友都不太熟悉,因为我在网上搜寻关于Remotetea的中文资料一篇也没有。既然如此,我就略为写几笔吧:)

  Remotetea是一个基于GNULGPL的开源的项目,它完全在Java 2/1.1平台上实现了ONC/RPC协议;由于是纯的100%的Java编写,所以不需要任何本地的库(native binary/libraries)。

简单的讲,它就是今天我们用于代替rpcgen而开发纯Java的RPC应用的工具。它的特点是:

  1. 100%的纯Java开发

  2. 完整的客户端功能,包括portmapper的访问。

  3. 完整的服务器端功能。

  4. 有为.x文件设计的纯Java协议编译工具,与rpcgen兼容。

  5. 基于Java的portmapper。

  6. 开源的代码;文档支持。

下载Remotetea,请在http://sourceforge.net/project/showfiles.php?group_id=87517 这里察看。下载bin包并解压,可以在classes文件夹中找到jrpcgen.jar、oncrpc.jar和portmap.jar。

四、简单层RPC应用的Java实现

  1. RPC的不同层次接口

  其实在开发客户端和服务器端的程序时,RPC提供了不同层次的开发例程调用接口。不同层次的接口提供了对RPC不同程度级别的控制。一般可分为五个等级的编程接口:简单层例程、高层例程、中间层例程、专家层例程、底层例程。其中,简单层是为快速开发RPC应用服务而设计的,面向普通RPC应用;关于其他层例程,在这里就暂不提及了。简单层其函数列表如下:

  Rpc_reg( )——在某一特定类型的传输层上注册一个过程,以作为提供服务的RPC程序。

  Rpc_call( )——可以远程调用特定主机上的特定过程。

  Rpc_Broadcast( ) ——向指定类型的所有传输端口上广播一个远程过程调用请求。

实现简单层时,便会用到我们刚才要提到的Remotetea。它可以将以类C语言语法的RPC语言进行源代码编译。在这里先提一下所谓“类C语言语法的RPC语言”。

  2. RPC语言及其编译

  RPC语言是XDR语言的控制扩展,与XDR语言一样在RFC1014中定义。句法的注意事项:

  a. 有两个保留字:“program”和“version”。

  b. 一个程序定义中不能出现两次版本名或版本号。

  c. 在一个版本的定义中,过程名称至多只能出现一次。

  d. 程序标识与常量和类型标识在同一空间中。

  e. 只有无符号常数才能被附值给程序,版本和过程。

所用到的文件后缀名为.x,可以称为x-文件。下面即是测试用的一个test.x文件的代码:

//////////////////////////////////////////////////////////

/*

* test.x: TEST Remote Procedure Function

*/

const MAXNAMELEN = 2048; /* maximum length of a test string */

typedef string test_string<MAXNAMELEN>; /* a directory entry */

/*

* THE TEST Remote Procedure Function program definition

*/

program TEST_RPC_FUNCTION_NUMBER


  version TEST_RPC_FUNCTION_VERSION

  { 
            mcps_string TEST_TEST(string) = 1; /* 这是过程号 */

mcps_string TEST_DO_PROCESS(string) = 2; /* 这是过程号 */

} = 1; /* 这是程序号 */

} = 0x20000001; /* 这是版本号 */

//////////////////////////////////////////////////////////

有这个文件以后,便可以在控制台敲入:java -jar jrpcgen test.x,执行后则会生成这几个文件(jrpcgen可以支持参数编译,请参照Remotetea的文档):testrpcClient.java、testrpc.java、testrpcServerStub.java、test_string.java。

  3. 生成文件说明

  通过用jrpcgen编译.x文件,将生成四个java文件,下面看看每个文件是干什么的。

  testrpc.java:这个文件相当是c中的.h头文件,它主要包括服务器和客户端程序变量、常量、类型等说明。

  test_string.java:从名字可以看出是字符串变量相关的,我想应该也可以这么讲吧。它其实是一个XDR例程,可以对在testrpc.java文件中定义的数据类型进行处理。

  testrpcClient.java:客户端的标准程序框架,提供一组特定的在x-文件中定义的远程过程。该框架类继承自OncRpcClientStub类:这是一个抽象类,用于在特定的客户端上构建ONC/RPC程序的基础类。

  testrpcServerStub.java:服务器端的标准程序框架,提供一组特定的在x-文件中定义的远程过程。该框架类继承自OncRpcServerStub类并实现OncRpcDispatchable接口:前者也是一个抽象类,用于在特定的服务器端上构建ONC/RPC程序的基础类;后者接口用于分配和处理来自客户端的ONC/RPC请求。

  4. 开发客户端程序

 有了以上的介绍,形势就开始明朗了。我们的客户端程序只需继承自生成的testrpcClient这个客户端框架类就可以了。代码如下:

//////////////////////////////////////////////////////////

import java.io.IOException;

import java.net.InetAddress;

import java.net.UnknownHostException;

import org.acplt.oncrpc.OncRpcClient;

import org.acplt.oncrpc.OncRpcException;

import org.acplt.oncrpc.OncRpcProtocols;

import testrpcClient ;

/*

* @author Noshoeman

*/

public class TestClient extends testrpcClient {

//可以有很多种构造函数,有较大灵活性,这里只写一种。

/**

* @param host

* @param port

* @param protocol

* @throws OncRpcException

* @throws IOException

*/

public TestClient(InetAddress host, int port, int protocol)

throws OncRpcException, IOException {

super(host, port, protocol);

//不需要做任何事。

}

/**

* @param args

* 这里是测试用的主函数

*/

public static void main(String[] args) {

//我们在单机测试,取得本地信息

System.out.println("--Start client.--");

InetAddress address = null;

try {

address = InetAddress.getLocalHost();

} catch (UnknownHostException e) {

e.printStackTrace();

}

//构造客户端并进行测试

try {

TestClient client = new TestClient(address,2023,OncRpcProtocols.ONCRPC_TCP);

client.TEST_DO_PROCESS ("Hello!");

client.close();

} catch (OncRpcException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

//////////////////////////////////////////////////////////

  整个过程没有什么需要多提的;注意的是在new客端的时候,端口2023是我随便写的,最后的OncRpcProtocols.ONCRPC_TCP,即是我们在前面的提到的约定底层的RPC传输通道,这里我们使用的是TCP(数值好像是6),也可以换为UDP。这样,一个简单的客户端测试程序就写好了。

  5. 开发服务器端程序

  和客户端程序的开发经验类似,也是继承自 testrpcServerStub 这个框架类。与客户端不同的是,在这里我们就需要实现远程过程的响应。示例代码如下:

//////////////////////////////////////////////////////////

import java.net.UnknownHostException;

import java.io.IOException;

import java.net.InetAddress;

import org.acplt.oncrpc.OncRpcException;

import test_string;

import testrpcServerStub;

/**

* @author Noshoeman

*/

public class TestServer extends testrpcServerStub {

/**

* @param bindAddr

* @param port

* @throws OncRpcException

* @throws IOException

*/

public TestServer(InetAddress bindAddr, int port) throws OncRpcException,

IOException {

super(bindAddr, port);

}

/*

* 这是第一个远程过程

* @see testrpcServerStub#TEST_TEST(java.lang.String)

*/

public mcps_string TEST_TEST(String arg1) {

System.out.println("This is test function! " + arg1);

return null;

}

/*

* 这是第二个远程过程

* @see testrpcServerStub#TEST_DO_PROCESS(java.lang.String)

*/

public mcps_string TEST_DO_PROCESS(String arg1) {

System.out.println("Got msg from client: " + arg1);

return null;

}

/**

* 服务器端的主函数

* @param args

*/

public static void main(String[] args) {

try {

System.out.println("Server starting...");

InetAddress address = null;

try {

address = InetAddress.getLocalHost();

System.out.println(address.toString());

} catch (UnknownHostException e) {

System.out.println("-------");

e.printStackTrace();

}

TestServer server = new TestServer(address, 2023);

System.out.println("Is server null? " + (server == null ? true : false));

server.run();

} catch (Exception e) {

e.printStackTrace();

}

}

}

//////////////////////////////////////////////////////////

  这样一来,服务器的测试例程就写好了。不愿意用ant来编译的“懒友”(me too),可以用eclipse建立两个相同的copy工程来分别编译并执行。先run服务器端,然后再run客户端,就可以看到当客户端run起来以后,服务器端的控制台出现字符:

Got msg from client: Hello!

恭喜,大功告成!

  6. 一些不愿意见到的事

  在这过程中,可能不会太顺利。但其实事情也并非不顺,只是多了些波澜,而往往就是这些波澜,让我们痛苦不堪……

  你可能无法正确调用jrpcgen,请注意Java相关的路径设置;如果是在1.5x下尝试始终有问题的话,请换1.4x试试。

  你可能会编译不过,请注意引入所需要的jar文件,在下载Remotetea的bin文件中有。

  但我在这里其实想说的是在run的时候出错。在run服务器端的时候,控制台打出了:Is server null?false这样的信息,但是在程序坚持一小会儿以后,就会出现:

org.acplt.oncrpc.OncRpcException: ONC/RPC portmap failure

at org.acplt.oncrpc.OncRpcPortmapClient.setPort(OncRpcPortmapClient.java:314)

at org.acplt.oncrpc.server.OncRpcUdpServerTransport.register(OncRpcUdpServerTransport.java:215)

at org.acplt.oncrpc.server.OncRpcServerStub.register(OncRpcServerStub.java:100)

at org.acplt.oncrpc.server.OncRpcServerStub.run(OncRpcServerStub.java:80)

at test.TestServer.main(TestServer.java:89)

  如果是遇到的这个错误,那么想说的是恭喜你,因为这表明你的程序本身已经没有问题了;问题只在于portmap。

  7. 如何解决ONC/RPC portmap failure

  要说清楚怕也是可以写一本小书了。在这里我们不打算细究这个问题,我要说的是如何解决上面遇到的问题。(当然没有遇到的话是甚好)

portmap 即“端口映射”,是一个server , 主要功能是转换 TCP/IP 通讯协定的port号变成 RPC program number , 因为这样客户端才能做RPC calls。

所以,显然,在之前Remotetea包中的一个portmap.jar,则是一个基于Java实现的,兼容Sun的portmap、protocol version 2的ONC/RPC portmap。

  这样的话,要解决其实很简单了。如果在Linux下,一般已经在/sbin/portmap下有了,man一下用法,其实就敲入portmap便好;如果在Windows下,则启用在Remotetea包中带的portmap.jar就可以了:java -jar jportmap.jar。如果在Windows下还有问题,请注意在系统管理的“服务(service)”里,有两个关于RPC的服务,打开再试试。当然,在Linux下,也可以同样使用这里的portmap.jar。

  唔,好了,就写到这里罢。

(转)简单的RPC java实现 .的更多相关文章

  1. 简单的RPC java实现

    RPC的名声大噪之时是在2003年,那一个“冲击波”病毒(Blaster Worm virus)袭卷全球的一年.而“冲击波”正是用着RPC这把刀来敲开了远程电脑的大门.当然RPC 有更多正面的应用,比 ...

  2. 分布式架构的基石.简单的 RPC 框架实现(JAVA)

    前言 RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式.允许像调用本地服务一样调用远程服务. 学习来源:<分布式系统架构:原理与实践> - 李林锋 1. ...

  3. Java实现简单的RPC框架(美团面试)

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  4. Java使用Netty实现简单的RPC

    造一个轮子,实现RPC调用 在写了一个Netty实现通信的简单例子后,萌发了自己实现RPC调用的想法,于是就开始进行了Netty-Rpc的工作,实现了一个简单的RPC调用工程. 如果也有兴趣动手造轮子 ...

  5. 最简单的RPC框架实现

    通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成: 1.服务提供者,运行再服务端,负责提供服务接口定义和服务实现类 2.服务发布者,运行再RPC服 ...

  6. 如何实现一个简单的RPC

    在如何给老婆解释什么是RPC中,我们讨论了RPC的实现思路. 那么这一次,就让我们通过代码来实现一个简单的RPC吧! RPC的实现原理 正如上一讲所说,RPC主要是为了解决的两个问题: 解决分布式系统 ...

  7. 教你用 Netty 实现一个简单的 RPC!

    众所周知,dubbo 底层使用了 Netty 作为网络通讯框架,而 Netty 的高性能我们之前也分析过源码,对他也算还是比较了解了. 今天我们就自己用 Netty 实现一个简单的 RPC 框架. 1 ...

  8. 自己用 Netty 实现一个简单的 RPC

    目录: 需求 设计 实现 创建 maven 项目,导入 Netty 4.1.16. 项目目录结构 设计接口 提供者相关实现 消费者相关实现 测试结果 总结 源码地址:github 地址 前言 众所周知 ...

  9. 一个简单的"RPC框架"代码分析

    0,服务接口定义---Echo.java /* * 定义了服务器提供的服务类型 */ public interface Echo { public String echo(String string) ...

随机推荐

  1. Yii2查询语句

    Yii2常用的查询: Yii2原生查询: $request = Yii::$app->request; $shopid = $request->post('shopid'); $gps = ...

  2. Android中父View和子view的点击事件的执行过程

    Android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解.  一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN- ...

  3. 记录一次项目中dubbo-admin实战部署

    环境: 1.centos7 2.jdk-7u76-linux-x64.tar.gz 2.tomcat:apache-tomcat-7.0.59.tar.gz 3.zookeeper-3.4.6.tar ...

  4. js设计模式——2.外观模式

    js设计模式——2.外观模式

  5. 杂项:SVN -u

    ylbtech-杂项:SVN 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   7.返回顶部   8.返回顶部   9.返回顶部   10 ...

  6. Appium 工作原理

    Appium - automation for mobile apps   一.Appium架构介绍 官网:www.appium.io 由SauceLab公司主持.并在Google的GATC2013会 ...

  7. linux进阶之路(三):vi/vim编辑器

    所有Linux都会内置vi,vim是vi的增强版本,被誉为"编辑之神",玩转vim可以让你完全脱离鼠标. vim可以分为两种模式: 普通模式:使用vim 文件名,进入普通模式.普通 ...

  8. mvc 前台传入后台

    转自:http://blog.csdn.net/huangyezi/article/details/45274553 一个很简单的分部视图,Model使用的是列表,再来看看调用该分部视图的action ...

  9. JUC源码分析-集合篇(十)LinkedTransferQueue

    JUC源码分析-集合篇(十)LinkedTransferQueue LinkedTransferQueue(LTQ) 相比 BlockingQueue 更进一步,生产者会一直阻塞直到所添加到队列的元素 ...

  10. 高级UI晋升之常用View(三)下篇

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章将从WebView来介绍常用View: 一.WebView介绍 Andro ...