Hadoop RPC源码分析

本文深入探讨了Hadoop RPC的工作机制,包括客户端和服务器端的交互流程,详细介绍了连接建立、数据传输及响应处理的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[java]  view plain copy
  1. Hadoop RPC :与传统的RPC程序相似,分为Server端和Client端二个部分,下面将分别介绍其工作机制  
  2.   
  3. 1.Client端分析:  
  4. |-->Client(Class<? extends Writable> valueClass, Configuration conf, SocketFactory factory)  
  5.   |-->分别设置 maxIdleTime、tcpNoDelay、pingInterval值  
  6.   |-->this.conf = conf; |设置configuation  
  7.   |-->this.socketFactory = factory; |设定socketFactory仓库,用于socket连接  
  8. |-->call(Writable param, InetSocketAddress addr,Class<?> protocol, UserGroupInformation ticket) |与服务器建立连接  
  9.   |-->Call call = new Call(param);  |new一个Call对象,Call对象对于在RPC中作为基础的传输单元  
  10.   |-->Connection connection = getConnection(addr, protocol, ticket, call);  
  11.   |-->connection.sendParam(call);   |发送call数据  
  12.   |--> while (!call.done)   
  13.     |--> call.wait();   |client端此处作同步处理,Server端实际为异步处理,在Response的setValue后被notify()唤醒  
  14. 1.1 获取连接方法  
  15.   |-->getConnection |从连接池里面获取一个连接对象,没有则创建一个  
  16.     |-->ConnectionId remoteId = new ConnectionId(addr, protocol, ticket);  |创建连接id,避免重复分配connectionID  
  17.     |-->connection = new Connection(remoteId); |利用conncetionId创建Connectin连接  
  18.     |-->connections.put(remoteId, connection); |缓存进连接池当中,Hashtable予以保存  
  19.     |-->connection.setupIOstreams(); |在这此真正建立连接  
  20. 1.1.1 connection.setupIOstreams()  |与服务器建立连接  
  21. |-->this.socket = socketFactory.createSocket();  
  22. |-->this.socket.setTcpNoDelay(tcpNoDelay);  
  23. |-->NetUtils.connect(this.socket, remoteId.getAddress(), 20000);  |用NIO的方式建立连接  
  24.     |-->SocketChannel ch = socket.getChannel();    |获取通道  
  25.     |-->socket.connect(endpoint, timeout);         |建立连接  
  26. |-->this.in = new DataInputStream(new BufferedInputStream         |分别设置输入输出流  
  27.             (new PingInputStream(NetUtils.getInputStream(socket))));          
  28. |-->this.out = new DataOutputStream  
  29.             (new BufferedOutputStream(NetUtils.getOutputStream(socket)));  
  30. |-->writeHeader();  |写Connection的头信息  
  31. |-->touch();        |更新最后活动时间,同步等待responser进行处理,client模式为同步机制  
  32. |-->start();        |connection继承了Thread,实际上执行了run()方法  
  33.     |-->run()     
  34.        |-->while (waitForWork())  |等待服务器连接端响应,或者关闭连接  
  35.                  |-->receiveResponse();  
  36.          |-->close()          |最后关闭  
  37. 1.2 connection.sendParam(call);  |发送数据  
  38. |-->d = new DataOutputBuffer();      
  39. |-->d.writeInt(call.id);          |首先写个call_id,用于标致唯一Call  
  40. |-->call.param.write(d);          |将Call中的Writable对象写入DataoutputStream中  
  41. |-->byte[] data = d.getData();       
  42. |-->int dataLength = d.getLength(); |获取data的长度  
  43. |-->out.writeInt(dataLength);       |往通道中先写长度,再写数据  
  44. |-->out.write(data, 0, dataLength);  
  45. |-->out.flush();                    |flush,使写入生效  
  46. |-->IOUtils.closeStream(d);         |利用IOUtils关闭流  
  47.   
  48. 1.3 receiveResponse() |接收服务器端响应,处理请求。  
  49. |-->int id = in.readInt();  
  50. |-->Call call = calls.get(id);  
  51. |-->int state = in.readInt();  |如果为success状态,则读取in流  
  52. |-->Writable value = ReflectionUtils.newInstance(valueClass, conf);  |利用反射生成一个Writable对象  
  53. |-->value.readFields(in);  
  54.     |-->this.value = value;  
  55.     |-->callComplete();  
  56.        |-->this.done = true;  
  57.        |-->notify();  |此时用于唤醒在client端call()调用时的call.wait()  
  58. |-->call.setValue(value);  
  59. |-->calls.remove(id);  
  60.   
  61.   
  62.   
  63. 2.Server端分析:  
  64. 2.1:namenode初始化Server端  
  65. |-->initialize()  |开启RPC的服务端  
  66.     |-->this.server = RPC.getServer(this, socAddr.getHostName(), socAddr.getPort(),  
  67.                                 handlerCount, false, conf);  
  68.     |-->this.server.start();  
  69.   
  70. 2.1.1:RPC.getServer  |RPC的静态类Server是Server服务器类的子类  
  71. |-->new Server(instance, conf, bindAddress, port, numHandlers, verbose);  
  72.     |-->super(bindAddress, port, Invocation.class, numHandlers, conf, classNameBase(instance.getClass().getName()));  
  73. 2.1.1.1:构建Server端类  
  74. |-->listener = new Listener();   |用于监听client端信息  
  75. |-->responder = new Responder(); |用于处理监听请求  
  76.   
  77. 2.2:this.server.start()  |启动server端服务,分别启动srepsonder,listener和handler类  
  78. |-->responder.start();   |注意启动顺序,确保监听器开始时即可处理响应请求  
  79. |-->listener.start();  
  80. |-->handlers = new Handler[handlerCount]; |handlerCount为handler线程个数   
  81. |-->for (int i = 0; i < handlerCount; i++)  
  82.     |-->handlers[i] = new Handler(i);  
  83.     |-->handlers[i].start();  
  84.   
  85. 2.3:Listener监听器  
  86. 2.3.1:构建Listener()  
  87. |-->address = new InetSocketAddress(bindAddress, port);  
  88. |-->acceptChannel = ServerSocketChannel.open();  
  89. |-->acceptChannel.configureBlocking(false);  
  90. |-->bind(acceptChannel.socket(), address, backlogLength);  
  91. |-->selector= Selector.open();  
  92. |-->acceptChannel.register(selector, SelectionKey.OP_ACCEPT);  
  93. |-->this.setDaemon(true);   |设置为后台守护进程  
  94.   
  95. 注:上述构建过程实际是简单的NIO server端启动过程,当client端请求过多时会出现瓶颈,后续版本增加了reader  
  96. |-->readers = new Reader[readThreads];    
  97. |-->readPool = Executors.newFixedThreadPool(readThreads);   |设定reader线程池大小  
  98. |-->for (int i = 0; i < readThreads; i++)             
  99.     |-->Selector readSelector = Selector.open();    
  100.     |-->Reader reader = new Reader(readSelector);    
  101.     |-->readers[i] = reader;    
  102.     |-->readPool.execute(reader);    
  103.   
  104. 2.3.2:Listener线程run()执行  
  105. |-->while (running)   |无限轮循  
  106. |-->selector.select();  
  107. |-->terator<SelectionKey> iter = selector.selectedKeys().iterator();  
  108. |-->while (iter.hasNext())  
  109.     |-->key = iter.next();  
  110.         |-->iter.remove();  
  111.     |-->if (key.isAcceptable())  
  112.         |-->doAccept(key);  |往通道里面写数据  
  113.     |-->else if (key.isReadable())  
  114.         |-->doRead(key);    |从通道里面读取数据  
  115.   
  116. 2.3.3:doAccept(key) |接受client请求  
  117. |-->ServerSocketChannel server = (ServerSocketChannel) key.channel();  
  118. |-->for (int i=0; i<10; i++)  |开启最多10个线程进行处理  
  119. |-->SocketChannel channel = server.accept();  
  120. |-->channel.configureBlocking(false);  
  121. |-->SelectionKey readKey = channel.register(selector, SelectionKey.OP_READ); |此channel重新注册至selector中,进行读取操作  
  122. |-->c = new Connection(readKey, channel, System.currentTimeMillis());  
  123. |-->readKey.attach(c);  |将connection附加到key当中,在is_readable中继续处理  
  124.   
  125. 2.3.4:doRead(key)   |处理client请求  
  126. |-->Connection c = (Connection)key.attachment();    |在accept时作为attach封装了Connection信息  
  127. |-->count = c.readAndProcess();  
  128.     |-->dataLengthBuffer   |如果未满或已经为空,则读取了一个RPC,否则继续读  
  129.     |-->channelRead(channel, dataLengthBuffer);  
  130.     |-->int version = versionBuffer.get(0);  |读取版本信息  
  131.     |-->data = ByteBuffer.allocate(dataLength);  |获取rpc大小  
  132.     |-->if (headerRead)  |判断headerRead是否读取,默认都会先发送header文件  
  133.         |-->processData();  
  134.     |-->else |先处理header头信息  
  135.         |-->processHeader();  
  136. |-->closeConnection(c);  
  137.   
  138. 2.3.4.1 processHeader()   
  139. |-->header.readFields(in);  
  140. |-->String protocolClassName = header.getProtocol();  |从头文件中获取protocl的处理class  
  141. |-->protocol = getProtocolClass(header.getProtocol(), conf);  
  142. |-->user = SecurityUtil.getSubject(header.getUgi());  |获取user信息,作为安全策略  
  143.   
  144. 2.3.4.2 processData()  
  145. |-->int id = dis.readInt();  
  146. |-->Writable param = ReflectionUtils.newInstance(paramClass, conf); |paramClass此时为namenode的class  
  147. |-->param.readFields(dis);  
  148. |-->Call call = new Call(id, param, this);   |在服务器端构建Call单元,并加入等待处理队列   
  149. |-->callQueue.put(call);       |加入callQueue队列后,工作将交由handler进行处理  
  150.   
  151.   
  152.   
  153. 2.4:Handler处理器  
  154. 2.4.1 构建Handler处理器 ,简单设置了Thread信息  
  155.     |-->this.setDaemon(true);  
  156.     |-->this.setName("IPC Server handler "+ instanceNumber + " on " + port);  
  157. 2.4.2 run()线程执行  
  158. |-->while (running)  
  159. |-->final Call call = callQueue.take();  
  160. |-->CurCall.set(call); |确保当前处理的call唯一  
  161. |-->value = Subject.doAs(call.connection.user, new PrivilegedExceptionAction<Writable>() {  
  162.                  @Override  
  163.                  public Writable run() throws Exception   
  164.                       return call(call.connection.protocol, call.param, call.timestamp);}  
  165. |-->setupResponse(buf, call,  (error == null) ? Status.SUCCESS : Status.ERROR,   
  166.                         value, errorClass, error);       |此时设置call的response信息  
  167. |-->responder.doRespond(call); |调用responder响应处理完成的call请求  
  168.               
  169. 2.4.3 执行call方法调用  
  170. call(call.connection.protocol, call.param, call.timestamp)  
  171. |-->Invocation call = (Invocation)param;  |此时param对象预先读取了method方法名及其参数  
  172. |-->Method method = protocol.getMethod(call.getMethodName(),  
  173.                                    call.getParameterClasses());  
  174. |-->method.setAccessible(true);  
  175. |-->Object value = method.invoke(instance, call.getParameters());  |利用反射获取方法调用的值  
  176. |-->return new ObjectWritable(method.getReturnType(), value);      |最后返回一个ObjectWritable的处理结果  
  177.   
  178. 2.4.4 responder.doRespond(call)  |属于responder的处理策略,提前分析  
  179. |-->call.connection.responseQueue.addLast(call);  
  180. |-->call.connection.responseQueue == 1 |call的responseQueue队列为1,则处理call单元  
  181.     |-->processResponse(call.connection.responseQueue, true);   |下节分析  
  182.   
  183. 2.5 Responser处理器  
  184.   
  185. 2.5.1 processResponse(responseQueue,inHandler)   |handler处理完call数据后,传递给Responser  
  186. |-->call = responseQueue.removeFirst();  
  187. |-->SocketChannel channel = call.connection.channel;  
  188. |-->int numBytes = channelWrite(channel, call.response);  
  189. |-->if (!call.response.hasRemaining()) |此时response中已经不存在call对象了  
  190.     |-->call.connection.decRpcCount();  
  191. |-->else   
  192.     |-->call.connection.responseQueue.addFirst(call);  
  193.     |-->writeSelector.wakeup();  |激活响应通道  
  194.         |-->channel.register(writeSelector, SelectionKey.OP_WRITE, call);  
  195.   
  196. 2.5.2 run() |Thread执行方法  
  197. |-->waitPending();       |等待wake up唤醒  
  198. |-->writeSelector.select(PURGE_INTERVAL);  |设置超时  
  199. |-->Iterator<SelectionKey> iter = writeSelector.selectedKeys().iterator();  
  200. |-->while (iter.hasNext())  
  201.     |-->SelectionKey key = iter.next();  
  202.     |-->iter.remove();  
  203.     |--> doAsyncWrite(key);  
  204.   
  205. |-->calls = new ArrayList<Call>(writeSelector.keys().size());  
  206. |-->while (iter.hasNext())  
  207.     |-->Call call = (Call)key.attachment();   
  208.     |-->calls.add(call);  
  209. |--> for(Call call : calls)   
  210.     |-->doPurge(call, now); |清除响应队列中长久未响应的call  
  211.   
  212.   
  213. 2.5.3  doAsyncWrite(key)  
  214. |-->Call call = (Call)key.attachment();  
  215. |-->if (processResponse(call.connection.responseQueue, false))  |调用processResponse进行响应  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值