2014-11-24--Hadoop的基础学习(六)--简单谈论RPC和Java世界中的RMI

1.分布式通信N种技术

是什么原因导致分布式通信技术变得如此繁多的?大家都知道,通信最本质的一点就是:通信的双方一定要规定好格式,也就是标准。让你发的东西可以让接到信息到人或者程序能够读得懂。正所谓,八仙过海,各显神通。都是过海,就要看你怎么过。面对不同的需求,就有不同的标准和方案。有的要简洁,有的要严谨,有的又是要结合别的技术。各有利弊吧。常用的分布式通信技术有如下:      

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的挑战.

2.RPC的简述

首先说明:RPC远程过程调用并不是Java语言所独有的,也就是说,还有别的平台和语言都有RPC的运用.核心是,调用过程代码并不是调用者本地运行,而是要实现调用者与被调用者两者之间的链接和通信.而RPC的基本通信模型是C/S模式的程序设计.即Client/Server进程间相互通信模型的一种同步通信形式,他对Client提供远程服务的过程抽象.其底层消息传递操作对Client是透明的。Client---->Caller.Server---->Callee.

2014-11-24--Hadoop的基础学习(六)--简单谈论RPC和Java世界中的RMI

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

3.进一步了解RPC

在Java中实现RPC通信的话,主要是用动态代理+Socket。而通过Object输入输出流,则需要序列号的声明。当时我对RPC也是一头雾水.感觉很抽象.飘在天上似的,但是看完这篇博客链接:http://javatar.iteye.com/blog/1123915 之后,就落地啦.自己重新整理其中的代码.让我自己更容易理解RPC.

在这里,贴出部分核心的代码.需要完整工程,留下邮箱。需要建立模拟的分布式环境及.

2014-11-24--Hadoop的基础学习(六)--简单谈论RPC和Java世界中的RMI

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);
        }
    }
}

4.Hadoop中的RPC机制

Hadoop的RPC机制采用叫做"架构层次的协议",有一整套作为协议的接口。如图:

2014-11-24--Hadoop的基础学习(六)--简单谈论RPC和Java世界中的RMI

介绍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);
	}
}

5.RMI(Remote Method Invocation)

要说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嗯,步骤很规范.

6.各种名词的解释和区别

通信标准或实现各有个的优点和缺点,在实际使用中根据具体情况合理选择.

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,不想说了。这些概念只有实战的时候,才能够知道.

结束..........................

你可能感兴趣的:(2014-11-24--Hadoop的基础学习(六)--简单谈论RPC和Java世界中的RMI)