从零学习Hadoop之Hadoop的RPC机制
说在前面的话
一般我们说了解的RPC(Remote Procedure Call,远程过程调用)机制都要面对两个问题:
- 对象调用方式;
- 序列/反序列化机制。
RPC架构如图所示:
Hadoop自己实现了简单的RPC组件,依赖于Hadoop Writable类型的支持。
Hadoop Writable接口要求每个实现类都要确保将本类的对象正确序列化(writeObject)与反序列化(readObject)。因此,Hadoop RPC使用Java动态代理与反射实现对象的调用方式(对应上述问题1),客户端到服务器数据的序列化与反序列化由Hadoop框架或者用户自己来实现,也就是数据组装是定制的(根据需求继承Writable接口)(对应上述问题2)。
RPC的实现流程
简单来说,Hadoop RPC = 动态代理 + 定制的二进制流。
远程的对象拥有固定的接口,这个接口用户也是可见的,只是真正的实现(Object)只在服务端。用户如果想使用哪个实现,调用过程是:
- 先根据那个接口动态代理生成一个代理对象;
- 调用这个代理对象;
- 用户的请求被RPC捕捉到;
- 然后包装成调用请求,序列化成数据流发送到服务端;
- 服务端从数据流中解析出调用请求;
- 然后根据用户所希望调用的接口;
- 再把调用结果返回给客户端
接口:
- package com.ifcoding.proxy;
- public interface JieKou {
- public int add(int a,int b);
- public int sub(int a,int b);
- }
Object:
- package com.ifcoding.proxy;
- public class BeiDaiLiZhe implements JieKou {
- @Override
- public int add(int a, int b) {
- return a+b;
- }
- @Override
- public int sub(int a, int b) {
- return a-b;
- }
- }
Handler:
- package com.ifcoding.proxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- public class DaiLiHandle implements InvocationHandler {
- private Object beiDaiLiZheDuiXiang;
- public DaiLiHandle(Object beiDaiLiZheDuiXiang) {
- this.beiDaiLiZheDuiXiang = beiDaiLiZheDuiXiang;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- System.out.println("before-----");
- Object res = method.invoke(beiDaiLiZheDuiXiang, args);
- System.out.println("after-----");
- return res;
- }
- }
测试:
- package com.ifcoding.proxy;
- import java.lang.reflect.Proxy;
- public class Test {
- public static void main(String args[]){
- BeiDaiLiZhe b = new BeiDaiLiZhe();
- DaiLiHandle handle = new DaiLiHandle(b);
- JieKou proxy = (JieKou)Proxy.newProxyInstance(b.getClass().getClassLoader(), b.getClass().getInterfaces(), handle); //创建代理对象
- System.out.println(proxy.add(1, 2));
- }
- }
上述只是简单一个利用动态代理和反射实现过程调用的例子。
RPC的实体模型
上面站在用户的角度,宏观地观察整个调用过程。这部分在细节上分析RPC都有哪些实体。为什么要提到实体?如果把RPC流程看作是流水线,这些实体就是一个个具体工作的工人,如果想深入了解流水线的处理,就要知道每个工人工作的职责及概况。
RPC在客户端的细节不多,需要注意的一点是:用户在调用代理对象时RPC是怎样拦截这次调用请求的。对动态代理清楚的都知道,创建代理对象时需要为它关联一个InvocationHandler,对代理对象的每次调用都会进入绑定的InvocationHandler中,RPC就从这里获取用户的请求。
RPC在服务端的模型由一系列实体组成,分别负责调用的整个流程。各个实体分工明确,各司其职。
- Listener:监听RPC Server的端口,如果客户端有连接请求到达,它就接收连接,然后把连接转发到某个Reader,让Reader读取那个连接的数据。如果有多个Reader,当有新的连接过来时,就在这些Reader间顺序分发。
- Reader:从某个客户端连接中读取数据流,把它转化成调用对象(call),然后就放到调用队列(call queue)里。
- Handler:真正做事的实体。它从调用队列中获取调用信息,然后反射调用真正的对象,得到结果,再把此次调用放到相应队列(response queue)里。
- Responder:不断地检查响应队列中是否有调用信息,如果有,就把调用的结果返回给客户端。
上图为RPC Server架构。
提示:整个调用流程中与网络有关的地方都是用NIO来处理的。
待续
后面将会基于RPC详细介绍一下文件在HDFS中的读取和写入。
1 条留言 访客:0 条 博主:0 条 引用: 1 条
来自外部的引用: 1 条