是什么原因导致分布式通信技术变得如此繁多的?大家都知道,通信最本质的一点就是:通信的双方一定要规定好格式,也就是标准。让你发的东西可以让接到信息到人或者程序能够读得懂。正所谓,八仙过海,各显神通。都是过海,就要看你怎么过。面对不同的需求,就有不同的标准和方案。有的要简洁,有的要严谨,有的又是要结合别的技术。各有利弊吧。常用的分布式通信技术有如下:
1.RPC(remote produce call):远程过程调用协议 2.RMI(remote method invocation):远程方法调用 3.JMS(java remote service):java远程服务 4.EJB(enterprise java bean):企业级JavaBean 5.CORBA(不太了解,过于重量级) 6.SOAP Web Service 7.HTTP Restful ------Restful Web Service 在本文结尾处,会对我知道的几种技术做简单的利弊分析.而在本文主要是解释说明RPC和RMI. 书籍:Java RESTful Web Service实战 引用:从架构风格的抽象高度来看,常见的分布式应用架构风格有三种:1.分布式对象(Distributed Objects,简称DO)、2.远程过程调用(Remote Procedure Call,简称RPC)、3.表述性状态转移(Representational State Transfer,简称REST); 在互联网中REST占据了统治地位,但在企业内网中却面临着DO和RPC的挑战. |
首先说明:RPC远程过程调用并不是Java语言所独有的,也就是说,还有别的平台和语言都有RPC的运用.核心是,调用过程代码并不是调用者本地运行,而是要实现调用者与被调用者两者之间的链接和通信.而RPC的基本通信模型是C/S模式的程序设计.即Client/Server进程间相互通信模型的一种同步通信形式,他对Client提供远程服务的过程抽象.其底层消息传递操作对Client是透明的。Client---->Caller.Server---->Callee.
归纳讲即是:首先是建立RPC服务,约定底层的RPC传输通道(UDP或是TCP)。客户端的调用参数根据传输前所提供的目的地址及RPC 上层应用程序号,通过底层的RPC传输通道转至相应的服务器,即RPC Application Porgramme Server。客户端随即处于等待状态,以服务器等待应答或Time Out超时信号。当服务器端获得了请求消息,会根据注册RPC时告诉RPC系统的程序入口地址执行相应的操作,并将结果返回至客户端。当一次RPC调用结束后,相应线程发送相应的信号,客户端程序便继续运行。有三个要素来标识唯一的远程过程:程序号、版本号、过程号。其中,程序号是用来区别一组相关的并且具有唯一过程号的远程过程;一个程序可以有一个或几个不同的版本;而每个版本的程序都包含一系列能被远程调用的过程。(这句比较拗口难读的话,一会儿用代码来解释)同一个版本可以包含有许多可供远程调用的过程,每个过程则有其唯一标示的过程号。通过版本的引入,使得不同版本下的 RPC能同时提供服务。 |
在Java中实现RPC通信的话,主要是用动态代理+Socket。而通过Object输入输出流,则需要序列号的声明。当时我对RPC也是一头雾水.感觉很抽象.飘在天上似的,但是看完这篇博客链接:http://javatar.iteye.com/blog/1123915 之后,就落地啦.自己重新整理其中的代码.让我自己更容易理解RPC.
在这里,贴出部分核心的代码.需要完整工程,留下邮箱。需要建立模拟的分布式环境及.
package com.codewatching.study.rpc.framework; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.ServerSocket; import java.net.Socket; /** * RpcFramework */ public class RpcFramework { /** * 暴露服务 * @param service 服务实现 * @param port 服务端口 * @throws Exception */ public static void export(final Object service, int port) throws Exception { if (service == null) throw new IllegalArgumentException("service instance == null"); if (port <= 0 || port > 65535) throw new IllegalArgumentException("Invalid port " + port); System.out.println("Export service " + service.getClass().getName() + " on port " + port); ServerSocket server = new ServerSocket(port); for(;;) { try { final Socket socket = server.accept(); new Thread(new Runnable() { @Override public void run() { try { try { ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); try { String methodName = input.readUTF(); Class<?>[] parameterTypes = (Class<?>[])input.readObject(); Object[] arguments = (Object[])input.readObject(); ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); try { Method method = service.getClass().getMethod(methodName, parameterTypes); Object result = method.invoke(service, arguments); output.writeObject(result); } catch (Throwable t) { output.writeObject(t); } finally { output.close(); } } finally { input.close(); } } finally { socket.close(); } } catch (Exception e) { e.printStackTrace(); } } }).start(); } catch (Exception e) { e.printStackTrace(); } } } /** * 引用服务 * * @param <T> 接口泛型 * @param interfaceClass 接口类型 * @param host 服务器主机名 * @param port 服务器端口 * @return 远程服务 * @throws Exception */ @SuppressWarnings("unchecked") public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception { if (interfaceClass == null) throw new IllegalArgumentException("Interface class == null"); if (! interfaceClass.isInterface()) throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!"); if (host == null || host.length() == 0) throw new IllegalArgumentException("Host == null!"); if (port <= 0 || port > 65535) throw new IllegalArgumentException("Invalid port " + port); System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port); return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { Socket socket = new Socket(host, port); try { ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); try { output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(arguments); ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); try { Object result = input.readObject(); if (result instanceof Throwable) { throw (Throwable) result; } return result; } finally { input.close(); } } finally { output.close(); } } finally { socket.close(); } } }); } }
package com.codewatching.study.rpc.test; import com.codewatching.study.rpc.framework.RpcFramework; import com.codewatching.study.rpc.service.HelloService; import com.codewatching.study.rpc.service.HelloServiceImpl; /** * RpcProvider */ public class RpcProvider { public static void main(String[] args) throws Exception { //指定HelloService的实现类 HelloService service = new HelloServiceImpl(); //提供RPC的服务(专门针对HelloService接口),端口号为12234 RpcFramework.export(service, 12234); } }
package com.codewatching.study.rpc.test; import java.io.IOException; import java.util.Properties; import com.codewatching.study.rpc.framework.RpcFramework; import com.codewatching.study.rpc.service.HelloService; /** * RpcConsumer */ public class RpcConsumer { private final static String path = "config.properties"; private final static String keyClassName = "service.interface.className"; private static String serviceClassName = null; //读取加载要引用的服务接口名称 static { Properties properties = new Properties(); try { properties.load(RpcConsumer.class.getClassLoader().getResourceAsStream(path)); serviceClassName = (String) properties.get(keyClassName); } catch (IOException e) { throw new ExceptionInInitializerError("sorry,the file of configure loading in error ..."); } } public static void main(String[] args) throws Exception { if(serviceClassName==null || "".equals(serviceClassName)){ //you can print the log return; } Class<?> clazz = Class.forName(serviceClassName); //引用指定名称的服务 HelloService service = (HelloService) RpcFramework.refer(clazz, "127.0.0.1", 12234); for (int i = 0; i < Integer.MAX_VALUE; i ++) { //执行调用服务 String hello = service.hello("World" + i); System.out.println(hello); Thread.sleep(1000); } } }
Hadoop的RPC机制采用叫做"架构层次的协议",有一整套作为协议的接口。如图:
介绍Hadoop中RPC的使用,关于源码级别的话,以后再学习吧. **再谈Hadoop中RPC** 1.启动服务,接口是BuyServicce,实例为BuyServiceImpl. |
package com.codewatching.rpc; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.ipc.RPC; import org.apache.hadoop.ipc.RPC.Builder; import org.apache.hadoop.ipc.RPC.Server; import com.codewatching.service.BuyService; import com.codewatching.service.impl.BuyServiceImpl; public class BuyRPCRunner { public static void main(String[] args) throws Exception{ //获取构造器 Builder builder = new RPC.Builder(new Configuration()); //获取Server对象 Server server = builder.setBindAddress("127.0.0.1") .setPort(10001) .setInstance(new BuyServiceImpl()) .setProtocol(BuyService.class) .build(); //启动RPC服务. server.start(); } }
2.BuyService的源码如下: |
package com.codewatching.service; public interface BuyService { public static final long versionID=10; //必须要使用序列号 public String pay(); }
3.客户端调用服务代码 |
package com.codewatching.controller; import java.net.InetSocketAddress; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.ipc.RPC; import com.codewatching.service.BuyService; public class BuyController { public static void main(String[] args) throws Exception{ InetSocketAddress addr = new InetSocketAddress("127.0.0.1",10001); //要什么参数,就给它什么. BuyService client = RPC.getProxy(BuyService.class, 10, addr, new Configuration()); String result = client.pay(); System.out.println(result); //关闭RPC链接 RPC.stopProxy(client); } }
要说RPC是可以面向过程的通信方式,而RMI则是一种面向对象的通讯方式.允许程序员使用远程对象来实现通信,并且支持多线程的服务,这是一次远程通讯的革命,为远程通信开辟新的里程碑。RMI(Remote Method Invocation)显得有些老旧,它是在Java-IDL加入J2SE之前被引入的。RMI开发流程与CORBA如出一辙(从出现的时间上无法确定RMI是否是按照CORBA规范定制的),因此,其开发过程相对比较烦琐,但是由于RMI是EJB的基础,因此,它在Java开发中具有十分重要的地位。现在介绍开发Java RMI的程序.
创建远程方法调用的5个步骤: 1. 定义一个扩展了Remote接口的接口,该接口中的每一个方法必须声明它将产生一个RemoteException异常; 2. 定义一个实现该接口的类; 3. 使用rmic程序生成远程实现所需的存根和框架; 4. 创建一个客户程序和服务器进行RMI调用; 5. 启动rmiregistry并运行自己的服务程序和客户程序。 |
参考博客:http://blog.csdn.net/leslies2/article/details/6436847。嗯,步骤很规范.
通信标准或实现各有个的优点和缺点,在实际使用中根据具体情况合理选择.
RPC(remote produce call):RPC是远程过程调用协议,它是基于C/S模型调用的机制,客户机向服务器端发送调用请求等待服务器应答,是一种典型的请求应答机制,大致过程可以理解为本地分布式对象向本机发请求,不用自己编写底层通信本机会通过网络向服务器发送请求,服务器对象接受参数后,经过处理再把处理后的结果发送回客户端。它是早期的支持分布式,缺点:rpc是面向过程的远程调用,不支持面向对象,所以现在用的人就少了。不支持异步调用. |
RMI(remote method invocation):rmi也是远程方法调用,似乎和RPC一样都是调用远程的方法,我们可以把RMI看作是用java语言实现了RPC协议,由于RPC不支持对象通信,这也是RMI比RPC的优越之处,支持对象传输。它遵循的不是SOAP协议,而是JRMP(java remote message protocol)转为java对象所制定的一个协议,可以运行在任何用java语言写的系统上,具有跨平台特性,它不能跨语言。既然用只支持java那么它也有了java对象的很多特性,如果垃圾回收、面向对象等。RMI 采用stubs (客户机)和 skeletons (框架)来进行远程对象(remote object)的通讯。stub 充当远程对象的客户端代理,有着和远程对象相同的远程接口,远程对象的调用实际是通过调用该对象的客户端代理对象stub来完成的,效果和调用本地对象一样。传输的数据一般是java对象,而不是XML格式的数据。 优点:支持分布式对象、跨平台,stubs/skeletons机制; 缺点:不能跨语言。 |
JMS(java remote service):jms是在各个java类(包括ejb类)之间传递消息的中间件,好比给我们送信的邮递员,在各个人之间收发信件。支持两种消息模型P2P和pub/stub,即点对点和发布订阅模型。传输的是一种消息移植机制,将消息从一个客户机移动到另一个终端。 优点:支持异步通信、消息produce和recept松耦合。 |
EJB(enterprise java bean):ejb是java EE 中的一个规范,该规范描述了分布式应用程序需要解决的问题,例如事务处理、安全、日志、分布式等,而同时呢,sun公司也实现了自己定义的这一个标准,相当于自己颁布一个标准然后,又给出了实现供别人使用,实现以很多API的方式提供给用的人。 ejb是按照java服务器接口定义的java类,可以理解为一个特殊的java类,放在容器里容器可以帮助该类管理事务、分布式、安全等,一般小的程序不会用到,只有大型分布式系统才会用到ejb,既然ejb是一个java类或是一个组件,颗粒较小,这也是与Webservice的区别之一,它就可以被其它一个或多个模块调用。 包含了三种类型的Bean,可以通过注释JPA一个规范来标记,其中有一种Bean,叫MDB消息驱动bean,它的通信机制涉及到了JMS协议。???不懂 ejb可以进行远程调用,但是不能够跨语言,ejb是同步调用,而平时我们说的的ejb异步调用指的是ejb的MDB异步通信. |
Web Service ----(包括Restful和SAOP):Web service是一种网络间跨平台、跨语言的分布式系统间通信的标准。传输的数据位XML、json等格式的数据,应用范围广。主要描述SAOP。书籍:Java RESTful Web Service实战. Web Service大体上分为5个层次: 1. Http传输信道 2. XML的数据格式 3. SOAP封装格式 4. WSDL的描述方式 5. UDDI UDDI是一种目录服务,企业可以使用它对Webservices进行注册和搜索 |
a.EJB与JMS的关系 b.它们其实是没有多大关系的,它们都是java EE的规范,ejb的一种类MDB实现了JMS规范,当然是先JMS规范的不止有ejb的mdb,比如apache ActiveMQ也实现了JMS规范,我们平时用的是实现了JMS的产品,而不是JMS规范。 c.Web service与EJB 对这两个常常有点迷惑人,因为他们都实现了分布式应用调用,虽然他们很相似但是还是有很多区别的,首先通信协议是不一样的,ejb采用rmi-iiop协议,Web service利用http协议传输数据,优点常识的都知道http协议支持的较广泛,从这点来看Web Service层次要高一些、俗话说站得高看得远。 d.Webservice主要关注于解决异构系统、不同语言系统通信,其关注的是分布式服务开发、着手点要高、站的角度高,而ejb可以看做是分布式编程平台,通过容器和组件,简化了程序开发、调试和部署等它关注的是分布式组件开发,粒度小。Web service可以看做是异构系统、异构语言系统间通信的一个标准,而ejb只属于J2EE规范的一部分。ejb底层用rmi-iiop协议进行通信,防火墙会阻止;web service是基于http协议进行通信,防火墙不会阻止。 e.SOA与Web Service SOA是面向服务体系架构,是一种编程思想,SOA不是Web Service,WebService是目前最适合实现SOA的技术 名词太多了,记不住。用到的时候,在翻吧.关于CORBA,不想说了。这些概念只有实战的时候,才能够知道. |
结束..........................