前段我们Hadoop
集群磁盘使用率比较高,部分硬盘空间使用超过了70%
,这部分服务器的DataNode
服务读写负载就比较高,造成部分数据同步任务因为读写超时而失败。具体场景和异常跟这篇博客里描述的很像。
DFSClient
客户端与HDFS
交互进行数据读写,需要从NameNode
拿到元数据信息,再和DataNode
交互,因此,超时时间也同时涉及这两个服务。以下分别是客户端与这两个服务交互的超时时间的解析。
客户端与NameNode
的操作超时时间由以下两个配置控制:
ipc.client.ping
:默认值是true
。当配置为true
时,客户端会尽力等待服务端响应,定期发送ping
消息,使得连接不会因为tcp timeout
而断开。当配置为false
时,客户端会使用配置项ipc.ping.interval
对应的值,作为timeout
时间,在该时间内没有得到响应,即会超时。
ipc.ping.interval
:以毫秒为单位,默认值是1分钟。当ipc.client.ping
配置为true
时,表示发送ping
消息的周期。当ipc.client.ping
设置为false
时,表示连接的超时时间。
当NameNode
节点处于满负载、NameNode
所在节点的CPU
100%
耗尽时,导致NameNode
无法响应,对于新连接到该NameNode
的HDFS
客户端,能够主备切换连接到另一个NameNode
,进行正常的操作,而对于已经连接到该NameNode
节点的HDFS
客户端可能会卡住,无法进行下一步操作。
HDFS
客户端到NameNode
的RPC
连接存在keep alive
机制,保持连接不会超时,尽力等待服务器的响应,因此导致已经连接的HDFS
客户端的操作会卡住。
对于已经卡住的HDFS
客户端,可以进行如下操作:
NameNode
响应,一旦NameNode
所在节点的CPU
利用率回落,NameNode
可以重新获得CPU
资源时,HDFS
客户端即可得到响应。HDFS
客户端所在的应用程序进程,使得HDFS
客户端重新连接空闲的NameNode
。为了避免该问题出现,在上述问题场景下,可以在客户端的配置文件core-site.xml
中做如下配置:
ipc.client.ping
配置为false
,让客户端会使用配置项ipc.ping.interval
对应的值,作为timeout
时间,在该时间内没有得到响应,即会超时ipc.ping.interval
配置一个较大的超时时间,避免服务繁忙时的超时,建议配置为900000
,单位为ms
DataNode
的读写超时时间由以下两个配置控制:
dfs.client.socket-timeout
。默认值1分钟。dfs.datanode.socket.write.timeout
。默认8分钟。以上配置是在HDFS
客户端进行设置,它们的默认值在org.apache.hadoop.hdfs.server.common.HdfsServerConstants
类里:
// Timeouts for communicating with DataNode for streaming writes/reads
// DataNode读写超时时间
public static final int READ_TIMEOUT = 60 * 1000;
public static final int READ_TIMEOUT_EXTENSION = 5 * 1000;
public static final int WRITE_TIMEOUT = 8 * 60 * 1000;
public static final int WRITE_TIMEOUT_EXTENSION = 5 * 1000; //for write pipeline
DataNode
读写超时的时间是跟DataNode
的数量有关的,最终是根据DataNode
的数量确定读写超时时间,计算方式是以读写超时时间的值乘以节点的数量,逻辑在org.apache.hadoop.hdfs.DFSClient
类里:
/**
* Return the timeout that clients should use when writing to datanodes.
* @param numNodes the number of nodes in the pipeline. 管道中节点的数量
*/
int getDatanodeWriteTimeout(int numNodes) {
return (dfsClientConf.confTime > 0) ?
(dfsClientConf.confTime + HdfsServerConstants.WRITE_TIMEOUT_EXTENSION * numNodes) : 0;
}
int getDatanodeReadTimeout(int numNodes) {
return dfsClientConf.socketTimeout > 0 ?
(HdfsServerConstants.READ_TIMEOUT_EXTENSION * numNodes +
dfsClientConf.socketTimeout) : 0;
}