“梦想感到一股清凉在夏日黄昏”
背景说明
在真正实验了解之前,这个参数一直认为是客户端的参数,只需要在客户端配置即可,事实也是如此。为什么还要探究呢?是因为今天客户那边提问了但是没有较具说服力的证据去证明。
在生产环境中,业务方用户明明设置了这个参数,在DataNode日志里面还是显示60s超时。异常如下截图
为了更加具有说服力去证明它是客户端生效的,这里不仅参考了社区版的hadoop3.0.0源码,也实际验证了这个观点,特此记录一下。
参数在源码中的位置
在HdfsClientConfigKeys.java中可以看到这里面的都是客户端参数。
从上面异常的截图因为cloudera对源码有所修改所以不能倒着从社区版源码位置依次找到执行顺序,但是可以根据起始和结束位置大致判断。
起始位置
org.apache.hadoop.hdfs.server.datanode.DataXceiver.run(DataXceiver.java:291)
DataXceiver.run方法中存在设置超时参数步骤
这步骤就是通过HdfsClientConfigKeys.java 中获取到的
peer.setReadTimeout(dnConf.socketTimeout);
结束位置
org.apache.hadoop.net.SocketIOWithTimeout.doIO(SocketIOWithTimeout.java:164)
在doIO 164行方法中执行了
throw new SocketTimeoutException(timeoutExceptionString(channel,
timeout, ops));
timeoutExceptionString 方法中打印了一句话就是我们看到的异常信息
return timeout + " millis timeout while " +
"waiting for channel to be ready for " +
waitingFor + ". ch : " + channel;
因为中间还有好多类没有依次理清楚,所以目前还是不能够很确定这个错误超时时间就是这个参数控制的以及是否只在客户端配置就能够生效的。那么下面实际的验证将直接论证这点。
复现此异常问题
这里我用了我本地的环境去验证,因为在客户的测试集群中有业务在运行所以不太方便。
这里我的客户端机器域名是test,它只有gataway角色。
这里我只修改了test机器的/etc/hadoop/conf/hdfs-site.xml中的dfs.client.socket-timeout为13毫秒
为了使DataNode角色服务压力过大
1、降低DataNode堆内存为50M
2、设置dfs.datanode.handler.count为1
3、频繁上传大文件到集群,然后再删除再上传(这里建议手工控制)
4、写个循环脚本频繁去下载大的文件
#!/bin/bash
while true
do
rm -rf /root/test/hdfst/scipt/teacher_baseinfo.txt
hdfs dfs -get /teacher_baseinfo.txt /root/test/hdfst/scipt/
sleep 2
done;
现在开始执行测试,在DN堆内存很快就会使用完,等待DN GC时候DN就会暂停其他操作那么就可以验证这个超时时间了,我这里等待了一会便复现了这个问题
java.net.SocketTimeoutException: 13 millis timeout while waiting for channel to be ready for read. ch : java.nio.channels.SocketChannel[connected local=/192.168.242.130:49342 remote=/192.168.242.111:1004]
at org.apache.hadoop.net.SocketIOWithTimeout.doIO(SocketIOWithTimeout.java:164)
at org.apache.hadoop.net.SocketInputStream.read(SocketInputStream.java:161)
at org.apache.hadoop.net.SocketInputStream.read(SocketInputStream.java:131)
at org.apache.hadoop.net.SocketInputStream.read(SocketInputStream.java:118)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at org.apache.hadoop.hdfs.protocolPB.PBHelperClient.vintPrefixed(PBHelperClient.java:537)
at org.apache.hadoop.hdfs.client.impl.BlockReaderRemote.newBlockReader(BlockReaderRemote.java:407)
at org.apache.hadoop.hdfs.client.impl.BlockReaderFactory.getRemoteBlockReader(BlockReaderFactory.java:848)
at org.apache.hadoop.hdfs.client.impl.BlockReaderFactory.getRemoteBlockReaderFromTcp(BlockReaderFactory.java:744)
at org.apache.hadoop.hdfs.client.impl.BlockReaderFactory.build(BlockReaderFactory.java:379)
at org.apache.hadoop.hdfs.DFSInputStream.getBlockReader(DFSInputStream.java:644)
at org.apache.hadoop.hdfs.DFSInputStream.blockSeekTo(DFSInputStream.java:575)
at org.apache.hadoop.hdfs.DFSInputStream.seekToBlockSource(DFSInputStream.java:1496)
at org.apache.hadoop.hdfs.DFSInputStream.readBuffer(DFSInputStream.java:728)
at org.apache.hadoop.hdfs.DFSInputStream.readWithStrategy(DFSInputStream.java:766)
at org.apache.hadoop.hdfs.DFSInputStream.read(DFSInputStream.java:829)
at java.io.DataInputStream.read(DataInputStream.java:100)
at org.apache.hadoop.io.IOUtils.copyBytes(IOUtils.java:98)
at org.apache.hadoop.io.IOUtils.copyBytes(IOUtils.java:66)
at org.apache.hadoop.io.IOUtils.copyBytes(IOUtils.java:127)
at org.apache.hadoop.fs.shell.CommandWithDestination$TargetFileSystem.writeStreamToFile(CommandWithDestination.java:485)
at org.apache.hadoop.fs.shell.CommandWithDestination.copyStreamToTarget(CommandWithDestination.java:407)
at org.apache.hadoop.fs.shell.CommandWithDestination.copyFileToTarget(CommandWithDestination.java:342)
at org.apache.hadoop.fs.shell.CommandWithDestination.processPath(CommandWithDestination.java:277)
at org.apache.hadoop.fs.shell.CommandWithDestination.processPath(CommandWithDestination.java:262)
at org.apache.hadoop.fs.shell.Command.processPathInternal(Command.java:367)
at org.apache.hadoop.fs.shell.Command.processPaths(Command.java:331)
at org.apache.hadoop.fs.shell.Command.processPathArgument(Command.java:304)
at org.apache.hadoop.fs.shell.CommandWithDestination.processPathArgument(CommandWithDestination.java:257)
at org.apache.hadoop.fs.shell.Command.processArgument(Command.java:286)
at org.apache.hadoop.fs.shell.Command.processArguments(Command.java:270)
at org.apache.hadoop.fs.shell.CommandWithDestination.processArguments(CommandWithDestination.java:228)
at org.apache.hadoop.fs.shell.FsCommand.processRawArguments(FsCommand.java:119)
at org.apache.hadoop.fs.shell.Command.run(Command.java:177)
at org.apache.hadoop.fs.FsShell.run(FsShell.java:326)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:76)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:90)
at org.apache.hadoop.fs.FsShell.main(FsShell.java:389)
从这可以看到 13 millis timeout 正对应了dfs.client.socket-timeout 参数的设置,而且这个没有修改服务层参数,这对于论证此参数控制着这个异常超时时间以及在客户端配置即生效更具说服力。
这时候再看看客户那边生产环境DN异常日志,虽然客户端设置了很长的超时参数,但是根据IP显示,是DN之间的互信访问才抛出的异常,而DN的机器配置并没有修改dfs.client.socket-timeout参数还是默认的60s,所以这里会被误会没有生效。
[connected local=/192.168.242.130:49342 remote=/192.168.242.111:1004]
这个意思是说明从local 130机器 访问 远程111机器 1004端口
以上便是证实dfs.client.socket-timeout 参数控制着这种异常的超时时间以及只在客户端配置即可生效。
同理对于在HdfsClientConfigKeys.java 文件的所有参数皆是如此,如块大小、副本数等,可以通过以下命令查看副本数 、块分布。
#查看副本数
hadoop fs -stat '%r' /文件名称
#查看块分布,然后到对应机器查看块大小
hdfs fsck /student.txt -files -blocks -racks
点个在看你最好看