[hadoop源码阅读][6]-org.apache.hadoop.ipc-ipc.client

本文详细解析Hadoop RPC客户端的工作原理,包括代理对象的创建、远程方法调用的具体流程、连接管理机制及异步同步模型。通过具体代码示例展示了客户端如何发起远程调用并与服务器交互。

1.client运行的流程

1)创建代理对象;

2)代理对象调用相应方法(invoke());

3)invoke调用client对象的call方法,向服务器发送请求(参数、方法);

4)再等待call方法的完成;

5)返回请求结果。

 

2.client主要的内部类

主要的几个类说明: 

  • 1. Call,表示一次rpc的调用请求
  • 2. Connection,表示一个client与server之间的连接,一个连接一个线程启动
  • 3. ConnectionId:连接的标记(包括server地址,协议,其他一些连接的配置项信息)
  • 4. ParallelCall:实现并行调用的请求
  • 5. ParallelResults:并行调用的执行结果

 

3.client调用过程

3.0一个实际的调用

在DFSclient中


   
return (ClientDatanodeProtocol)RPC.getProxy(ClientDatanodeProtocol. class , ClientDatanodeProtocol.versionID, addr, conf);

3.1生成代理


  
public static VersionedProtocol getProxy(Class <? extends VersionedProtocol > protocol, long clientVersion, InetSocketAddress addr, UserGroupInformation ticket, Configuration conf, SocketFactory factory, int rpcTimeout) throws IOException { …… VersionedProtocol proxy = (VersionedProtocol) Proxy.newProxyInstance(protocol.getClassLoader(), new Class[] { protocol }, new Invoker(protocol, addr, ticket, conf, factory, rpcTimeout)); …… return proxy; }

其中Invoker是一个实现了InvocationHandler 接口的类

 

3.2代理对象调用相应方法(invoke())

getProxy调用者,使用这个proxy进行任何protocol声明的函数调用,比如还是上例中DFSclient的例子,如果调用proxy.getBlockInfo(…);都会转化成调用Invoker类的invoke函数


  
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { …… ObjectWritable value = (ObjectWritable) client.call( new Invocation(method, args), remoteId); …… return value.get(); }

Invocation 用于封装方法名和参数,作为数据传输层。远程调用的主要关键就是Invocation实现了Writable接口,Invocation在write(DataOutput out)函数中将调用的methodName写入到out,将调用方法的参数个数写入out ,同时逐个将参数的className写入out,最后将所有参数逐个写入out,这也就决定了通过RPC实现调用的方法中的参数要么是简单类型,要么是String,要么是实现了Writable接口的类(参数自己知道如何序列化到stream),要么是数组(数组的元素也必须为简单类型,String,实现了Writable接口的类)。

Invocation序列化参数的实现是通过如下函数实现的:org.apache.hadoop.io.ObjectWritable.writeObject


  
public void write(DataOutput out) throws IOException { UTF8.writeString(out, methodName); out.writeInt(parameterClasses.length); for ( int i = 0 ; i < parameterClasses.length; i ++ ) { ObjectWritable.writeObject(out, parameters[i], parameterClasses[i], conf); } }

 

3.3invoke调用client对象的call方法,向服务器发送请求(参数、方法)


  
public Writable call(Writable param, ConnectionId remoteId) throws InterruptedException, IOException { Call call = new Call(param); // new Invocation(method, args) Connection connection = getConnection(remoteId, call); // 获得连接对象 可见一个client可以有多个connection connection.sendParam(call); // 将Invocation(method, args)的函数名,参数序列化发送到server端 while ( ! call.done) { call.wait(); // wait for the result 调用client的线程在此阻塞 } return call.value; // 返回调用结果 }

 

3.4获得连接对象getConnection


  
private Connection getConnection(ConnectionId remoteId, Call call) throws IOException, InterruptedException { Connection connection; do { synchronized (connections) { connection = connections.get(remoteId); if (connection == null ) { connection = new Connection(remoteId); connections.put(remoteId, connection); } } } while ( ! connection.addCall(call)); // 可见 一个connection 可以有多个调用call connection.setupIOstreams(); // 实际进行连接 每个connection都新起一个线程 return connection; }

 

3.5 connection的线程等待接受结果


  
public void run() { try { while (waitForWork()) // 超时检测等条件 connection close { // wait here for work - read or close connection receiveResponse(); } } close(); } private void receiveResponse() { try { int id = in.readInt(); // try to read an id Call call = calls.get(id); int state = in.readInt(); // read call status if (state == Status.SUCCESS.state) { Writable value = ReflectionUtils.newInstance(valueClass, conf); value.readFields(in); // 将结果反序列化 call.setValue(value); // 在这里 calls.remove(id); } } catch (IOException e) { markClosed(e); } }

 

3.6返回结果,通知client线程


  
public synchronized void setValue(Writable value) { this .value = value; callComplete(); } protected synchronized void callComplete() { this .done = true ; notify(); // notify caller }

 

4. 异步/同步模型

Hadoop的RPC对外的接口其实是同步的,但是,RPC的内部实现其实是异步消息机制。hadoop用线程wait/notify机制实现异步转同步,发送请求(call)之后wait请求处理完毕,接收完响应(connection.receiveResponse())之后notify,notify()方法在call.setValue中。但现在有一个问题,一个connection有多个call。可能同时有多个call在等待接收消息,那么是当client接收到response后,怎样确认它到底是之前哪个request的response呢?这个就是依靠的connection中的一个HashTable<Integer, Call>了,其中的Integer是用来标识Call,这样就可以将request和response对应上了。

 

5.时序图

1

2

 

6.client类图

client

 

 

 

7.参考

http://weixiaolu.iteye.com/blog/1504898

http://www.wikieno.com/2012/02/hadoop-ipc-client/

http://caibinbupt.iteye.com/blog/280790

http://blog.youkuaiyun.com/sxf_824/article/details/4842153

http://blog.youkuaiyun.com/historyasamirror/article/details/6159248

<think>我们正在处理一个Hadoop RPC异常问题,具体是RPC响应数据长度超过最大限制。根据Hadoop的默认配置,RPC响应的大小是有限制的,当响应数据超过这个限制时就会抛出异常。 首先,我们需要了解Hadoop中控制RPC响应大小的配置参数。在Hadoop中,RPC的最大响应大小由`ipc.maximum.response.length`参数控制,其默认值通常为100MB(104857600字节)。如果响应数据超过这个值,就会抛出异常。 解决方案是增加这个参数的值。但是,需要注意的是,增加这个值可能会增加内存使用,因此需要根据实际情况调整。 另外,我们还需要考虑是否真的需要传输这么大的数据。如果是因为数据量过大,可能需要优化程序,比如减少单次RPC传输的数据量,或者采用其他方式传输大数据(例如使用HDFS)。 步骤: 1.Hadoop配置文件中(如core-site.xml)增加或修改以下配置: ```xml <property> <name>ipc.maximum.response.length</name> <value>新的最大值(单位:字节)</value> </property> ``` 例如,设置为200MB:`<value>209715200</value>` 2. 重启相关的Hadoop服务,使配置生效。 3. 如果问题依然存在,可能需要考虑应用程序的设计,尝试减少单次RPC返回的数据量。 另外,根据引用的内容,我们还可以看到一些其他相关的操作,比如使用HLog工具进行日志分割(引用[1]),但这与RPC响应长度问题没有直接关系。引用[2]和引用[3]提到了HDFS的缓存管理和Hive元数据存储位置的问题,也与当前问题无关。 因此,我们主要关注调整`ipc.maximum.response.length`参数。 但是,请注意,这个参数在Hadoop的不同版本中可能有所不同。在较新的版本中,可能使用不同的参数名。例如,在Hadoop 3.x中,该参数仍然有效。如果调整后无效,请查阅对应版本的文档。 另外,如果是在HBase中遇到类似问题,也可能需要调整HBase相关的RPC配置,因为HBase有自己的RPC设置。但当前问题是Hadoop RPC,所以先调整Hadoop的配置。 最后,需要提醒的是,增加RPC响应大小限制只是临时解决方案,长期方案应该考虑优化数据传输,避免单次响应数据过大。 相关问题: 1. 如何监控Hadoop RPC的性能和响应大小? 2. Hadoop中还有哪些与RPC相关的配置参数? 3. 除了调整配置参数,还有哪些方法可以避免RPC响应数据过大? 希望以上解决方案能帮助您解决问题。</think>### Hadoop RPC响应数据长度超限解决方案 当Hadoop RPC响应超过最大数据长度限制时,会抛出类似`org.apache.hadoop.ipc.RpcException: RPC response exceeds maximum data length`的异常。以下是系统化的解决方案: #### 1. **调整核心配置参数** 在`core-site.xml`中增加RPC响应限制: ```xml <property> <name>ipc.maximum.response.length</name> <value>268435456</value> <!-- 默认100MB, 建议设为256MB --> <description>Max allowed RPC response size in bytes</description> </property> ``` - 此参数控制RPC服务器返回数据的最大字节数 - 需**重启所有Hadoop服务**生效 #### 2. **优化数据传输** - **分页查询**:对大数据集结果进行分块返回 ```java // MapReduce示例 job.setMaxMapAttempts(4); // 减少单次传输量 job.setNumReduceTasks(0); // 避免Reduce阶段数据聚合 ``` - **压缩传输**:启用RPC数据压缩 ```xml <property> <name>ipc.server.tcpnodelay</name> <value>true</value> </property> ``` #### 3. **检查数据倾斜问题** - 使用Hadoop计数器分析数据分布: ```shell $ hadoop job -counter <job_id> 'org.apache.hadoop.mapred.Task$Counter' ``` - 若存在单节点数据过载: - 增加`mapreduce.input.fileinputformat.split.maxsize` - 使用`Partitioner`重新分配Reduce负载 #### 4. **升级硬件与配置** - **JVM调优**:增加DataNode/NameNode堆内存 ```xml <!-- hdfs-site.xml --> <property> <name>dfs.datanode.max.transfer.threads</name> <value>16384</value> <!-- 提高并发处理能力 --> </property> ``` - **网络优化**: - 确认交换机支持巨帧(Jumbo Frame) - 检查`ipc.client.connect.max.retries`配置 #### 5. **替代方案** - 对超大数据集改用HDFS直接读写: ```java Path outputPath = new Path("hdfs://namenode:8020/output"); FSDataOutputStream out = fs.create(outputPath); ``` - 使用Apache Arrow实现**零拷贝数据传输**[^1] > **关键提示**:修改配置后需同步更新集群所有节点的`core-site.xml`,并验证防火墙是否允许大报文传输(`netstat -s | grep 'packet too big'`)。 --- ### 相关问题 1. Hadoop RPC响应超限时如何获取详细的堆栈跟踪信息? 2. 在Kerberos安全环境下调整RPC参数有哪些特殊注意事项? 3. 如何监控Hadoop集群的RPC响应大小分布? 4. 数据倾斜导致RPC异常时,有哪些自动化检测工具可用? 5. Hadoop 3.x与2.x版本在RPC长度限制处理上有何差异? [^1]: 详见Apache Arrow内存数据格式文档:https://arrow.apache.org/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值