Hadoop RPC
来源:互联网 发布:算法设计克林伯格pdf 编辑:程序博客网 时间:2024/05/22 17:40
终于进入RPC模块了,网上有很多基于早期hadoop的RPC机制分析,分析都很到位,今天我就带着大家走走源码(使用版本2.4.0)。本文采用深度遍历的方法分析源码,不知道你们习不习惯
前奏:
RPC是开发中抽出来的组件,如果不使用RPC,那么在分布式调用中流程是不是介个样子:服务器端是不是先创建ServerSocket在指定的ip地址和端口上监听,客户端创建到远程连接的Socket;好了,socket套接字完毕。然后我们可以从套接字拿到输入输出流InputStream,OutputStream吧。接着客户端就可以往输出流写序列化的参数,服务器端将读到的输入流中参数流反序列化成程序支持的类型,然后将处理的结果再序列化到输出流,发送给客户端。每次都这样写是不是很麻烦?
分析步骤:
从客户端发送请求开始,到服务器端返回,看当初google工程师是如何设计的
主要代码:
RPC的类都放在ipc包下面,ipc是进程间通信的意思。前面我们知道了动态代理,分析RPC先从InvocationHandler入手。在程序中我们发现了RpcInvocationHandler继承了InvocationHandler,而RpcInvocationHandler接口有多个实现类,其中两个重要实现类是ProtobufRPCEngine和WritableRPCEngine,很明显这跟两种序列化框架有关,Writable是hadoop当初自己实现的序列化,Protobuf是谷歌的序列化(单独抽一篇文章讲解)。为了不走偏,咱们从WritableRPCEngine开始。在WritableRpcEngine类中,我们看到了静态Invoker类实现了InvocationHandler。
下面是Invoker类的invoke方法:
代码1
public Object invoke(Object proxy, Method method, Object[] args) // 终于找到你了 invoke throws Throwable { long startTime = 0; if (LOG.isDebugEnabled()) { // 是否启用了debug startTime = Time.now(); } // ObjectWritable是Writable中最复杂的一类序列化 // 在invoke方法中,他调用了client的call方法,先看下参数第一个是使用的序列化框架类型 // 第二个参数是Invocation对象,叫VO(Value Object数据传输层) // 第三个参数是remoteId,类型是ConnectionId 表示唯一确定一个连接(那么可复用) ObjectWritable value = (ObjectWritable) client.call(RPC.RpcKind.RPC_WRITABLE, new Invocation(method, args), remoteId); if (LOG.isDebugEnabled()) { long callTime = Time.now() - startTime; LOG.debug("Call: " + method.getName() + " " + callTime); } return value.get(); }Client也是在ipc包下,跟RPC类平级,进入它的call方法看看呗
进去之后它又调用了重载方法,进入它的重载方法
代码2
public Writable call(RPC.RpcKind rpcKind, Writable rpcRequest, ConnectionId remoteId, int serviceClass) throws IOException { final Call call = createCall(rpcKind, rpcRequest); // 创建call对象,代表一次rpc调用,就不进去详细看了 Connection connection = getConnection(remoteId, call, serviceClass); // 获取连接它是怎么获取连接的呢,进如getConnection方法看看吧
方法里边前面主要是判断连接池里有没有符合自己的连接,有的话就复用(是不是跟前面的ConnectId呼应了),没有就新建,然后放到队列里
connections.put(remoteId, connection);
然后你会发现在后面有行代码
connection.setupIOstreams(); // 进去看看吧,好像是建立IO
代码3
private synchronized void setupIOstreams() { if (socket != null || shouldCloseConnection.get()) { // 是的,你没看错,socket,多么激动人心啊 return; } try { if (LOG.isDebugEnabled()) { LOG.debug("Connecting to "+server); } short numRetries = 0; final short MAX_RETRIES = 5; Random rand = null; while (true) { setupConnection(); // 貌似这一行比较关键,进去吧 InputStream inStream = NetUtils.getInputStream(socket); // 看到了吧,获取输入流哦 OutputStream outStream = NetUtils.getOutputStream(socket); // 获取输出流 writeConnectionHeader(outStream);下面就进入setupConnection瞧一瞧
代码4
private synchronized void setupConnection() throws IOException { short ioFailures = 0; short timeoutFailures = 0; while (true) { try { // 终于等到你,我亲爱的套接字啊 // 使用了工厂方法模式, // createSocket()代码我拷其中一个实现类StandardSocketFactory里面的 // StandardSocketFactory// public Socket createSocket(InetAddress addr, int port) throws IOException {//// Socket socket = createSocket();// socket.connect(new InetSocketAddress(addr, port));// return socket;// } this.socket = socketFactory.createSocket(); this.socket.setTcpNoDelay(tcpNoDelay); this.socket.setKeepAlive(true);上面是不是有了连接的建立与IO的建立
下面再看看客户端请求时发送的数据情况,再回到Client中的call方法
代码5
public Writable call(RPC.RpcKind rpcKind, Writable rpcRequest, ConnectionId remoteId, int serviceClass) throws IOException { final Call call = createCall(rpcKind, rpcRequest); // 创建call对象,代表一次rpc调用,就不进去详细看了 Connection connection = getConnection(remoteId, call, serviceClass); // 获取连接 try { connection.sendRpcRequest(call); // 发送rpc请求。注意:hadoop1中是connection.sendParam(call);
呵呵,继续跟进sendRpcRequest方法,核心代码如下
try { synchronized (Connection.this.out) { // 二话不说,先上锁 if (shouldCloseConnection.get()) { // 判断是否关闭 return; } if (LOG.isDebugEnabled()) // ... LOG.debug(getName() + " sending #" + call.id); byte[] data = d.getData(); // d是DataOutputBuffer,缓冲输出流 int totalLength = d.getLength(); // 得到流的长度 out.writeInt(totalLength); // 先写入int型数值,即下面待发送数据长度 out.write(data, 0, totalLength);// 发送数据 out.flush(); // 冲厕所,哈哈 } }为了把client的讲完,接着我们把client接收服务器端的response讲一下,接收过程在run方法中
try { while (waitForWork()) {//wait here for work - read or close connection receiveRpcResponse(); } }receiveRpcResponse方法走起
try { int totalLen = in.readInt(); // 读取数据长度的一个整型数值 RpcResponseHeaderProto header = RpcResponseHeaderProto.parseDelimitedFrom(in); // 也许你迷茫了,谷歌的序列化也在这里出现了 checkResponse(header); int headerLen = header.getSerializedSize(); headerLen += CodedOutputStream.computeRawVarint32Size(headerLen); int callId = header.getCallId(); if (LOG.isDebugEnabled()) LOG.debug(getName() + " got value #" + callId); Call call = calls.get(callId); RpcStatusProto status = header.getStatus(); if (status == RpcStatusProto.SUCCESS) { // 判断是不是调用成功 Writable value = ReflectionUtils.newInstance(valueClass, conf); // 哈哈,前面java的反射我们分析过了, // 是不是很熟悉的感觉 // 根据valueClass和配置信息动态创建实例 value.readFields(in); // 读取输入流并赋值 calls.remove(callId); // 成功了就要把调用从队列中移除 call.setRpcResponse(value); // 把对象放到call中,到这里是不是发现就像本地调用,一直操作本地的call好了,server端留下篇再讲吧
- hadoop RPC
- hadoop RPC
- Hadoop RPC
- Hadoop RPC
- Hadoop RPC
- rpc hadoop
- Hadoop RPC
- Hadoop RPC
- hadoop rpc
- Hadoop RPC
- Hadoop RPC
- Hadoop RPC
- Hadoop RPC
- hadoop rpc
- hadoop RPC
- hadoop RPC
- <hadoop>hadoop RPC框架
- Hadoop RPC 实例
- C语言算法(二)
- qt相关博客
- 无法获得锁 /var/lib/dpkg/lock - open (11: 资源暂时不可用)
- NSURLSession实现断点下载
- EXT.NET的布局方式
- Hadoop RPC
- 二十四.SVC模式设置
- HttpServletRequest 对象
- Android最佳实践之:StrictMode介绍
- C语言(三),数据类型,运算符
- 动手实现一个磁盘高速缓存:准备工作(二)
- RS232与RS485的功能与区别!
- Qt的Tab选项卡
- 第十三周项目四(2)