1. Hadoop安装过程中常见问题
一、启动namenode服务后,web页面依然无法访问
1、启动namenode服务,
指令:start-all.sh
'''
[root@hadoop1 hadoop-3.2.1]# start-all.sh
Starting namenodes on [hadoop1]
Starting datanodes
Starting secondary namenodes [hadoop1]
Starting resourcemanager
Starting nodemanagers
ERROR: Refusing to run as root: roo account is not found. Aborting.
'''
2、查看namenode服务是否启动,
'''
[root@hadoop1 hadoop-3.2.1]# jps
8130 Jps
7494 ResourceManager
6871 NameNode
7244 SecondaryNameNode
'''
3、查看后台监听端口
'''
[root@hadoop1 hadoop-3.2.1]# netstat -nltp |grep 6871
tcp 0 0 192.168.43.250:9000 0.0.0.0:* LISTEN 6871/java
tcp 0 0 0.0.0.0:9870 0.0.0.0:* LISTEN 态态 6871/java
'''
4、查看web是否可以访问,发现web页面无法访问
5、检查防火墙设置,可以看帆燃源到hadoop1服务器已经禁用了除本机外的其他多有服务访问,
[root@hadoop1 hadoop-3.2.1]# service iptables status
表格:filter
Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
2 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0
3 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
4 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
5 REJECT all -- 0.0.0.0/0 0.0.0.0/段野0 reject-with icmp-host-prohibited
Chain FORWARD (policy ACCEPT)
num target prot opt source destination
1 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
6、关闭防火墙,并把防火墙设置为开启不启动
centos6:
关闭防火墙:service iptables stop
设置开启不启动防火墙:chkconfig iptables off
centos7:
关闭防火墙:systemctl stop firewalld.service
设置开启不启动防火墙:systemctl disable firewalld.service
7、检查web已经可以正常显示
8、如果上面的操作依然无法访问的话,需要查看一下主机的hosts文件 是否有配置域名映射
二、开启datanode指令时出现waring
[root@hadoop1 hadoop-3.2.1]# hadoop-daemon.sh start datanode
WARNING: Use of this script to start HDFS daemons is deprecated.
WARNING: Attempting to execute replacement "hdfs --daemon start" instead.
主要是2.7版本的hadoop已经把hadoop命令改为hdfs命令了,所以尝试使用
指令:hdfs --daemon start datanode
'''
[root@hadoop2 hadoop-3.2.1]# hdfs --daemon start datanode
[root@hadoop2 hadoop-3.2.1]# jps
4064 Jps
4033 DataNode
2922 ResourceManager
'''
三、使用root配置的hadoop并启动会出现报错
错误:
Starting namenodes on [master]
ERROR: Attempting to operate on hdfs namenode as root
ERROR: but there is no HDFS_NAMENODE_USER defined. Aborting operation.
Starting datanodes
ERROR: Attempting to operate on hdfs datanode as root
ERROR: but there is no HDFS_DATANODE_USER defined. Aborting operation.
Starting secondary namenodes [slave1]
ERROR: Attempting to operate on hdfs secondarynamenode as root
ERROR: but there is no HDFS_SECONDARYNAMENODE_USER defined. Aborting operation.
原因分析:由于root没有start-dfs.sh和 stop-dfs.sh脚本的执行权限,在这两个脚本的开头加上如下参数,给root赋予执行权限即可:
HDFS_DATANODE_USER=root
HADOOP_SECURE_DN_USER=hdfs /* 后续版本这边需要修改为 HDFS_DATANODE_SECURE_USER=hdfs
HDFS_NAMENODE_USER=root
HDFS_SECONDARYNAMENODE_USER=root
start-yarn.sh,stop-yarn.sh顶部也需添加以下
YARN_RESOURCEMANAGER_USER=root
HADOOP_SECURE_DN_USER=yarn /* 后续版本这边需要修改为 HDFS_DATANODE_SECURE_USER=hdfs
YARN_NODEMANAGER_USER=root
4、hdfs运行指令时出现warn警告提示:
2019-11-13 00:07:58,517 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
该警告信息主要是由于是依赖库的问题
我们对静态库查看下依赖:看下依赖是否都正常:
通过指令 ldd libhadoop.so.1.0.0
'''
./libhadoop.so.1.0.0: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./libhadoop.so.1.0.0)
linux-vdso.so.1 => (0x00007fff369ff000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f3caa7ea000)
libc.so.6 => /lib64/libc.so.6 (0x00007f3caa455000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3caac1b000)
'''
可以看到是glibc 版本的问题:
我们再确认下:
GLIBC_2.14找不到,现在检查系统的glibc库, ldd --version 即可检查。
输入命令:
'''
ldd --version
ldd (GNU libc) 2.12
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for ing conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
'''
还可以直接确认下glibc 目前支持的版本:
通过如下查询方法:
'''
strings /lib64/libc.so.6|grep GLIBC
GLIBC_2.2.5
GLIBC_2.2.6
GLIBC_2.3
GLIBC_2.3.2
GLIBC_2.3.3
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.5
GLIBC_2.6
GLIBC_2.7
GLIBC_2.8
GLIBC_2.9
GLIBC_2.10
GLIBC_2.11
GLIBC_2.12
GLIBC_PRIVATE
'''
可以看到目前只支持到 2.12
解决办法有两个
1、升级 glibc 库
2、屏蔽hadoop提示这个告警
直接在log4j日志中去除告警信息。在$HADOOP_HOME/etc/hadoop/log4j.properties文件中添加
log4j.logger.org.apache.hadoop.util.NativeCodeLoader=ERROR
2. web调用hdfs出现找不到Configuration这个类(hadoop2.6)
有时袜历候通过IDE引伏好腊入的jar包并没有一缺滑起编译到Bin目录去,我用intellijIdea遇到过好几次。
3. Python使用hdfs存放文件时报Proxy error: 502 Server dropped connection解决方案
Python3 使用hdfs分布式文件储存系统
from pyhdfs import *
client = HdfsClient(hosts="testhdfs.org, 50070",
user_name="web_crawler") # 创建一个连接
client.get_home_directory() # 获取hdfs根路径
client.listdir(PATH) # 获取hdfs指定路径下的文件列表
client._from_local(file_path, hdfs_path, overwrite=True) # 把本地文件拷贝到服务器,不支持文件夹;overwrite=True表示存在则覆盖
client.delete(PATH, recursive=True) # 删除指定文件
hdfs_path必须包含文件名及其后缀,不握歼然不会成功
如果连接
HdfsClient
报错
Traceback (most recent call last):
File "C:\Users\billl\AppData\Local\Continuum\anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "
client.get_home_directory()
File "C:\Users\billl\AppData\Local\Continuum\anaconda3\lib\site-packages\pyhdfs.py", line 565, in get_home_directory
return _json(self._get('/', 'GETHOMEDIRECTORY', **kwargs))['Path']
File "C:\Users\billl\AppData\Local\Continuum\anaconda3\lib\site-packages\pyhdfs.py", line 391, in _get
return self._request('get', *args, **kwargs)
伍皮并 File "C:\Users\billl\AppData\Local\Continuum\anaconda3\lib\site-packages\pyhdfs.py", line 377, in _request
_check_response(response, expected_status)
File "C:\Users\billl\AppData\Local\Continuum\anaconda3\lib\site-packages\pyhdfs.py", line 799, in _check_response
腔迹 remote_exception = _json(response)['RemoteException']
File "C:\Users\billl\AppData\Local\Continuum\anaconda3\lib\site-packages\pyhdfs.py", line 793, in _json
"Expected JSON. Is WebHDFS enabled? Got {!r}".format(response.text))
pyhdfs.HdfsException: Expected JSON. Is WebHDFS enabled? Got '\n\n\n\n
502 Server dropped connection
\n
The following error occurred while trying to access http://%2050070:50070/webhdfs/v1/?user.name=web_crawler&op=GETHOMEDIRECTORY :
\n 502 Server dropped connection
\n
Generated Fri, 21 Dec 2018 02:03:18 GMT by Polipo on .\n\r\n'
则一般是访问认证错误,可能原因是账户密码不正确或者无权限,或者本地网络不在可访问名单中
4. namenode正处于safemode状态,怎么处理
dfs.cluster.administrators
hdfs
dfs.block.access.token.enable
true
dfs.datanode.failed.volumes.tolerated
0
dfs.replication.max
50
dfs.datanode..reserved
1073741824
dfs.blockreport.initialDelay
120
dfs.datanode.data.dir
file:///data/hadoop/hdfs/dn
dfs.client.read.shortcircuit
true
dfs.datanode.max.transfer.threads
4096
dfs.namenode.http-address
hadoop01:50070
dfs.client.read.shortcircuit.streams.cache.size
4096
<!-- 在客户端读册尺轿取前会创建一个FileinputStreamCache,就是由前两个参数控制大小和过期时间,
dfs.client.read.shortcircuit.streams.cache.size和dfs.client.read.shortcircuit.streams.cache.expiry.ms -->
dfs.namenode.avoid.write.stale.datanode
true
<!-- 表明是否要避免写为“过时”的心跳州肆消息尚未收到的NameNode超过指定的时间间隔数据节点。写操困亮作将避免使用陈旧的数据节点,除非多数据节点的配置比例
(dfs.namenode.write.stale.datanode.ratio)被标记为失效。见dfs.namenode.avoid.read.stale.datanode为读取一个类似的设置。 -->
dfs.namenode.avoid.read.stale.datanode
true
dfs.namenode.stale.datanode.interval
30000
<!--dfs.client.read.shortcircuit.streams.cache.size和dfs.client.read.shortcircuit.streams.cache.expiry.ms
以及dfs.client.read.shortcircuit.skip.checksum和dfs.client.read.shortcircuit.buffer.size.其中,
在客户端读取前会创建一个FileinputStreamCache,就是由前两个参数控制大小和过期时间的,其中key就是Datanode+block;
后两个参数就是决定是否跳过校验以及校验的块大小.-->
dfs.permissions.enabled
true
dfs.datanode.ipc.address
0.0.0.0:8010
dfs.namenode.name.dir
file:///data/hadoop/hdfs/nn
dfs.journalnode.http-address
0.0.0.0:8480
dfs.heartbeat.interval
3
dfs.datanode.data.dir.perm
750
fs.permissions.umask-mode
022
dfs.datanode.balance.bandwidthPerSec
6250000
dfs.namenode.accesstime.precision
0
dfs.namenode.write.stale.datanode.ratio
1.0f
dfs.namenode.checkpoint.dir
file:///data/hadoop/hdfs/snn
dfs.journalnode.edits.dir
/grid/0/hdfs/journal
dfs.blocksize
134217728
<!-- 2.X 版本默认值:134217728 说明: 这个就是hdfs里一个文件块的大小了,默认128M;太大的话会有较少map同时计算,
太小的话也浪费可用map个数资源,而且文件太小namenode就浪费内存多。对于较大集群,可设为256MB,根据需要进行设置。-->
dfs.replication
3
dfs.block.local-path-access.user
hbase
dfs.datanode.address
0.0.0.0:50010
dfs.datanode.http.address
0.0.0.0:50075
dfs.https.namenode.https-address
c6401.ambari.apache.org:50470
dfs.webhdfs.enabled
true
dfs.namenode.handler.count
100
dfs.namenode.secondary.http-address
hadoop02:50090
dfs.permissions.superusergroup
hdfs
dfs.namenode.safemode.threshold-pct
1.0f
dfs.domain.socket.path
/var/lib/hadoop-hdfs/dn_socket
5. hadoop集群搭好 为什么hdfs端口500070打不开
1、启动hadoop后,要看哪些界面或功能之前,建议先用jps或是netstat -ano看下相应的端口是否已经开启了,如果端口没开启,是肯定看不了相应的界面的。 试下看吧。
6. hadoop web界面显示blocksize为0
当向HDFS上大卜写文件时,槐山可以通过设置dfs.blocksize配置项来设置文件的block size,这导致HDFS上不同文件滚明穗的block size是不同的。
7. HDFS 权限管理
HDFS Permissions Guide
HDFS 实现了文件和目录的权限模型,很多跟POSIX模型共享。 每个文件、目录都关联到用户、组。文件、目录对于拥有者、组中的其他用户、其他用户有着不同的权限设置。对于文件,读取需要 r 权限,写入/追加需要 w 权限。对于目录,列出目录内容需要 r 权限,创建、删除文件或目录需要 w 权限,访问目录子项需要 x 权限。
与POSIX模型不同,因为没有可执行文件,所以文件没有 setuid or setgid bits。对于目录,也没有 setuid or setgid bits作为简化。粘滞位 sticky bit 可以被作用于目录,放置除superuser、目录所有者、文件所有者之外的任何用户删除、移动目录内的文件。粘滞位 sticky bit 对于文件不生效。 文件或目录的权限我们称为 mod 。一般来说,mdoe的展示使用Unix风格,比如描述的8进制数使用。当创建一个文件/目录后,它的所有者即为客户端进程的用户,它的组是父目录的组(BSD规则)。
HDFS 还支持POSIX ACLs (Access Control Lists),给指定用户、组配置更新颗粒度的规则来加强权限管理。ACLs稍后会详细介绍。
每个访问HDFS的客户端进程身份都有两部分组成:用户名,组列表。无论什么时候,HDFS都必须会客户端进程访问的文件或目录foo做权限检查,
如果权限校验失败,客户端操作失败。
自 Hadoop 0.22 起,Hadoop 支持两种不同的操作模式来判断用户的身份,通过 hadoop.security.authentication 属性指定:
不管是哪种模式,正型橡用户身份机制都是HDFS的外部机制。HDFS中没有用于创建用户、建立组、处理用户凭据的规定。
一旦username被判定,如上,groups list将由group mapping service 判断,配置在 hadoop.security.group.mapping 属性。参见 Hadoop Groups Mapping 获取更多内容。
每次HDFS操作都需要用户拥有指定权限(读,写,执行的组合),通过文件的 ownership,group membership or 其他权限进行授权。 一个操作会进行路径多个组件的权限检查,而不仅是最后一个组件。另外,一些操作依赖于路径owner的检租悔查。
所有的操作都需要遍历访问(traversal access)。遍历访问需要对路径上的所有存在的组件拥有执行举旁权限,最终路径组件除外。比如,任一访问 /foo/bar/baz 的操作,调用者需要拥有 /, /foo and /foo/bar 的可执行权限。
[1] 如果调用使用overwrite配置,并且该路径已经存在文件,那么 create 操作只需要对最终路径组件拥有写权限即可。
[2] 如果设置了 sticky bit ,对父目录写权限的检查操作,同样也会检查ownership。
[3] 调用 setOwner 来改变文件的拥有着需要 HDFS super-user 访问权限。变更组不需要HDFS super-user 访问权限,但是调用者必须是文件的拥有者并且是指定组的成员。
每个文件、目录操作都会将完全路径发送给NameNode,对每个操作都会沿着path进行权限检查。客户端框架隐式的将用户身份与到NameNode的连接关联,减少对现有客户端API的改动的需求。常见一种情况,当某文件的一个操作已经成功完成了,再操作的时候会失败,因为该路径上的文件、目录已经不存在了。举个例子,当客户端第一次开始读取一个文件,它向NameNode发出第一个请求,以发现文件的第一个块的位置。第二个请求查找其他的块可能会失败。另一方面,删除一个文件并不会撤回客户端对该文件的访问,该客户端已经知道该文件的块。通过添加权限,客户端对文件的访问可以在请求之间被撤回。同样,变更权限不会撤回客户端的访问,该客户端已经知道文件的块。
如果权限检查失败,所有使用路径参数的方法都会抛出 AccessControlException 。
新方法:
新文件、目录的mode受umask设置限制,umask设置是配置项。
当使用现有方法 create(path, …) (不带权限参数),新文件的 mode 是 0666 & ^umask 。
当使用新方法 create(path, permission, …) (带权限参数 P) ,新文件的 mode 是 P & ^umask & 0666 。
当使用现有方法 mkdirs(path) (不带权限参数)创建一个新目录, 新目录的 mode 是 0777 & ^umask 。
当使用新方法 mkdirs(path, permission) (带权限参数 P), 新目录的 mode 是 P & ^umask & 0777 。
新操作:
谁启动NameNode,谁就是super-user。 super-user可以执行任意操作,权限校验从不失败。HDFS super-user 不必是NameNode 主机上的super-user,也不是说集群内的说有主机都需要有这个super-user。
如果在个人电脑上实验运行HDFS,为方便起见无须任何配置该用户即成为安装的super-user。
另外,管理员还可以通过配置参数标识一个特定组。该组内的成员也是super-users。
默认,web server的身份是一个可配置项。即,NameNode不知道真实用户的身份,但是web server以管理员选定的用户的身份(用户/组)行动。 除非选择的身份匹配super-user,部分命名空间是不可以被web server访问的。
除了传统的POSIX权限模型,HDFS还支持POSIX ACLs (Access Control Lists)。ACLs 对于实现区分用户、组的自然组织层次结构的权限需求非常有用。ACL提供了一个方法,可以给指定用户、组设置不同的权限,而不仅仅是文件的拥有着和所属组。
默认,ACLs的支持是关闭的,并且NameNode不允许创建ACLs。要开启ACLs的支持,在NameNode配置内设置 dfs.namenode.acls.enabled 为 true。
一个ACL由一系列ACL entries组成。每条ACL entry 命名了特定用户/组,并授于/拒绝 读、写、执行 权限。比如:
ACL entries 由 type ,可选的 name 和 permission 组成。为了便于展示,‘:’ 用作分隔符。 在这个范例内,文件的owner有read-write权限,文件的group有read-execute权限,others有读取权限。因此,等同于设置文件的权限为654。
另外,有2个扩展ACL entries给用户 bruce 和组 sales,并全部赋予所有权限。 mask 是一个特殊的ACL entry,过滤给所有命名的user entries 和命名的group entries 以及非命名的group entry的权限。 在范例内, mask 仅有写权限,并且我们可以看到几个ACL entries的有效权限被相应的过滤了。
每个ACL都必要要有一个 mask 。如果在设置ACL的时候用户没有提供 mask ,那么会通过计算来自动插入,计算将被过滤的所有entries的权限的并集。
在拥有ACL的文件上运行 chmod 实际上改变了 mask 的权限。既然 mask 作为过滤器,这有效的约束了所有扩展ACLs的权限,而不仅是变更group entry并且可能会丢失其他扩展ACL entries。
模型还有效区分了 “access ACL” 和 “default ACL” 。
“access ACL” ,定义权限检查期间强制执行的规则; “default ACL” ,定义新子文件或子目录创建期间自动接受的规则。
比如:
只有目录可以拥有默认 ACL。当一个新文件或者子目录创建,它会自动的拷贝父级的默认ACL作为自己的ACL。新子目录还将其作为默认 ACL。这样,当新目录创建时,默认 ACL 会被复制到该文件系统数的任意深度层。
新子级的ACL的确切权限值将由mode参数进行过滤。考虑到默认umask 是 022,则新目录是755,新文件是644。mode参数给unnamed user (file owner), the mask and other过滤过滤拷贝过来的权限值。 使用这个特定范例ACL,并创建一个mod 755的子目录,该mode过滤器对最终结果没有影响。但是,如果我们考虑创建一个mode 644 的文件,mode过滤器会导致新文件的ACL接受unnamed user(file owner)的读写权限,mask的读权限,以及其他的读权限。 该mask意味着named user bruce 和 named group sales 的有效权限是只读。
注意,拷贝发生在创建新文件或子目录的时候。对父级默认ACL的后续修改不会影响存在的子级。
默认 ACL 必须拥有所有必要的ACL entries,包括unnamed user (file owner), unnamed group (file group) and other entries。当设置默认ACL的时候,如果用户没有指定其中某个entry,那么entries会自动插入,通过拷贝访问ACL相应的权限、如果没有访问ACL则拷贝permission bits来实现。默认ACL必须拥有mask。如上所属,如果没有指定会自动插入一个计算的值。
对拥有ACL的文件,权限的检查算法变更为:
最佳实践依赖传统的权限位来实现大多数权限需求,并定义较少数量的ACL来加强,用一些异常规则来扩充权限位。与只有权限位的文件相比,具有ACL的文件在NameNode中会增加内存开销。
新方法:
public void modifyAclEntries(Path path, List<AclEntry> aclSpec) throws IOException;
public void removeAclEntries(Path path, List<AclEntry> aclSpec) throws IOException;
public void public void removeDefaultAcl(Path path) throws IOException;
public void removeAcl(Path path) throws IOException;
public void setAcl(Path path, List<AclEntry> aclSpec) throws IOException;
public AclStatus getAclStatus(Path path) throws IOException;
参考 File System Shell 文档获取命令的全部内容。
如果是,则使用这里描述的权限系统。如果否,关闭权限检查,但是其他行为不变。从一个参数值切换到其他值不会改变mode、文件或目录的owner or group。无论权限是开还是关, chmod 、 chgrp 、 chown and setfacl 总是会检查权限。 这些功能仅在权限上下文有用,因此不存在向后兼容问题。此外,这允许管理员可靠的设置所有者和权限,在打开常规权限检查前。
web server使用的用户名。设置为super-user允许所有客户端看到任何内容。改为其他未使用的身份允许web客户端仅可以查看"other"权限可见的内容。其他将其他组添加到逗号分隔列表内。
super-users 的组。
当创建文件和目录时使用的 umask 。对于配置文件,可以使用十进制值 18。
集群的管理员被配置为ACL。这控制谁可以访问HDFS默认的servlets 等。
设置为true,开启HDFS ACLs (Access Control Lists)的支持。默认,ACLs是关闭的。当ACLs关闭的时候,NameNode拒绝所有设置ACL的企图。 .
设置为true,开启 POSIX风格的ACL继承。默认开启。当它被启动时,并且create请求来自于兼容客户端,NameNode会从父目录应用默认ACL来创建mode并忽视客户端umask。如果没有默认ACL,它将应用客户端umask。
8. hadoop的web管理界面打不开怎么办
在虚拟机上安装了Hadoop,安装情况如下:
192.168.220.129 master
192.168.220.130 slave1
192.168.220.131 slave2
配置好各个ip后,在虚拟系统中可以访问,http://192/168.220.129:50070 ,但是在笔记本的window上不能访问
使用命令查看如下:
9. HDFS客户端无法及时addBlock和关闭文件问题分析
现网运行过程中,某些高负载集群的 NN 频繁打印下面的 “block is COMMITTED but not COMPLETE" 日志,且客户端经常会关闭文件失败,导致业务异常退出,如下所示:
这实际是一个 block 无法及时到达 COMPLETE 状态的问题,在 HDFS 中,一个 block 只有达到最小副本数之后,才能变为 COMPLETE 状态。HDFS 默认的最小副本数是1,也就是说,NameNode 收到至少1个 DataNode 的上宴帆报之后,这个 block 才能达到 COMPLETE 状态,表示这个 block 已经正式写入完毕,其内容此时已成功固化到磁盘。
如果一个文件的 block 不能及时达到 COMPLETE 状态,则会导致
这两个集群是负载较高的两个集群,共同点是 NameNode 都会频繁的打印上面那种 “block is COMMITTED but not COMPLETE” 日志,但这两个集群又有所不同:
以那个导致客户端出错的 block 为例(block ID 为:blk_16141305305),可以看到,客户端有5次重试,加上第一次的正常操作,一共尝试了6次 addBlock(或 completeFile),但全部未成功,这将直接导致客户端写文件失败(或关闭文件失败):
这个 block 涉及到两个 DN,以9.179.163.164 这个 DN 为例,可以看到,DN IO 线程在 16:46:16 写完 block 触发增量上报之后,一直过了45s 到了16:47:01,DN 心跳线程才开始真正向 NN 发送增量上报 RPC 消息,这很不正常,正常情况应该是立即发送才对(另外结合上面的 NN 日志可以看到,DN 发送增量上报 IBR 之后,就被 NN 同时在16:47:01处理完毕,可见此时 NN 没有问题):
进一步分析 DN 的 IBR 上报记录,可以看到:心跳线程在 16:46:07 到 16:47:01 之间,这54s之内,根本没有进行任何 IBR 上报,这也导致 16:47:01一下子积攒了很多 block,那么问题很清楚了,在这长达54s 的时间内,DN 心跳线程到底在干什么?
DN 心跳线程循环执行 BPServiceActor.offerService() 函数,在这个函数里,它循环做下面的事情:
现在的问题集中在:DN 在处理这次 IBR 之前,一共54s 内,到底在干嘛?
首先想到的就是 jstack,但是现在是事后分析,已经没法再 jstack 了。好在这个问题集群其实一直在持续性的出现这种文件不能及时关闭的问题,所以,现在再 jstack,也来的及,具体步骤:
这次倒是比较顺利的拿到了结果:
既然找到了原因,那么解决起来就比较容易了,通过上面的分析,核心的原因还是 FsDatasetImpl.datasetLock 这把全局互斥锁实在是太难抢了,导致心跳线程在 getStorageReports() 时长时间等这把锁(最终是 getDfsUsed() )。但实际上,心跳线程此时其实不需要这把锁,因为它拿到锁之后,要访问的数据结构是一个 ConcurrentHashMap,要历祥灶知道 ConcurrentHashMap 本身即是一肢扮个支持并发读写的结构,访问它根本不需要额外再加什么锁。
到底为止,改法其实出来了,DN 心跳线程在 getDfsUsed() 时,不用加 FsDatasetImpl.datasetLock 这把全局互斥锁。
历尽千辛万苦,终于找到了原因,但是当我要去改的时候,发现 hdfs 3.x 已经改了这个地方(改动的目的是解决另外一个问题,但却顺带着也解决了我们这次的问题),所以 backport 就可以了,具体 jira 为:HDFS-7060 Avoid taking locks when sending heartbeats from the DataNode
从这个问题的分析过程,也可以看出,像 NameNode 和 DataNode 这种高并发、大吞吐的多线程任务,在功能方面,可以不断的增加新特性,但在性能方面,最终的瓶颈往往会落到某个全局锁上(或者虽然不是全局但范围特别广的锁),这时候争锁才是导致多线程无法完全发挥潜力、整体吞吐量下降的阿克琉斯之踵。更加难受的是,争锁的困境往往很难通过小修小补解决,而是需要架构层面的重构,这又进一步加大了优化的成本。典型的比如 HDFS NN 和 DN 中的两把全局锁,对整体性能的影已经是一个旷日持久的问题,但直到现在,也依然没有得到解决:
DN 心跳线程争锁是一个老生常谈的问题,除了上面已经合入的 HDFS-7060 之外,社区还有其他几个 Patch 也需要合入:
10. HDFS文件
Hadoop支持的文件系统知培由很多(见下图),HDFS只是其中一种实现。Java抽象类 org.apache.hadoop.fs.FileSystem 定义了Hadoop中一个文件系统的客户端接口,并且该抽象类有几个具体实现。Hadoop一般使用URI(下图)方案来选取合适的文件系统实例进行交互。
特别的,HDFS文件系统的操作可以使用 FsSystem shell 、客户端(http rest api、Java api、C api等)。
FsSystem shell 的用法基本同本地shell类似,命令可参考 FsSystem shell
Hadoop是用Java写的,通过Java Api( FileSystem 类)可以调用大部分Hadoop文件系统的交互操作。更详细的介绍可参考 hadoop Filesystem 。
非Java开发的应用可以使用由WebHDFS协议提供的HTTP REST API,但是HTTP比原生的Java客户端要慢,所以不到万不得已尽量不要使用HTTP传输特大数据。通过HTTP来访问HDFS有两种方法:
两种如图
在第一种情况中,namenode和datanode内嵌的web服务作为WebHDFS的端节点运行(是否启用WebHDFS可通过dfs.webhdfs.enabled设置,默认为true)。文件元数据在namenode上,文件读写操作首先被发往namenode,有namenode发送一个HTTP重定向至某个客户端,指示以流的方式传输文件数据的目的或源datanode。
第二种方法依靠一个或多个独立代理服务器通过HTTP访问HDFS。所有集群的网络通信都需要通过代理,因此客户端从来不直接访问namenode或datanode。使用代理后可以使用更严格的防火墙策略和带宽策略。
HttpFs代理提供和WebHDFS相同的HTTP接口,这样客户端能够通过webhdfs URI访问搭晌唯接口。HttpFS代理启动独立于namenode和datanode的守护进程,使用httpfs.sh 脚本,默认在一个不同的端口上监听(14000)。
下图描述了
读文件时客户端与 HDFS 中的 namenode, datanode 之间的数据流动。
对上图的解释如下:
在读取过程中, 如果 FSDataInputStream 在和一个 datanode 进行交流时出现了一个错误,他就去试一试下一个最接近的块,他当然也会记住刚才发生错误的 datanode 以至于之后不会再在这个 datanode 上进行没必要的尝试。 DFSInputStream 也会在 datanode 上传输出的数据上核查检查数(checknums).如果损坏的块被发现了, DFSInputStream 就试图从另一个拥有备份的 datanode 中去读取备份块中的数据。
在这个设计中一个重要的方面就是客户端直接从 datanode 上检索数据,并通过 namenode 指导来得到每一个块的最佳 datanode。这种设计允许 HDFS 扩展大量的并发客户谨液端,因为数据传输只是集群上的所有 datanode 展开的。期间,namenode 仅仅只需要服务于获取块位置的请求(块位置信息是存放在内存中,所以效率很高)。如果不这样设计,随着客户端数据量的增长,数据服务就会很快成为一个瓶颈。
我们知道,相对于客户端(之后就是 maprece task 了),块的位置有以下可能性:
我们认为他们对于客户端的带宽递减,距离递增(括号中表示距离)。示意图如下:
如果集群中的机器都在同一个机架上,我们无需其他配置,若集群比较复杂,由于hadoop无法自动发现网络拓扑,所以需要额外配置网络拓扑。
基本读取程序,将文件内容输出到console
FileSystemCat
随机读取
展开原码
下图描述了写文件时客户端与 HDFS 中的 namenode, datanode 之间的数据流动。
对上图的解释如下:
如果在任何一个 datanode 在写入数据的时候失败了,接下来所做的一切对客户端都是透明的:首先, pipeline 被关闭,在确认队列中的剩下的包会被添加进数据队列的起始位置上,以至于在失败的节点下游的任 何节点都不会丢失任何的包。然后与 namenode 联系后,当前在一个好的 datanode 会联系 namenode, 给失败节点上还未写完的块生成一个新的标识ID, 以至于如果这个失败的 datanode 不久后恢复了,这个不完整的块将会被删除。失败节点会从 pipeline 中移除,然后剩下两个好的 datanode 会组成一个的新的 pipeline ,剩下的 这些块的包(也就是刚才放在数据队列队首的包)会继续写进 pipeline 中好的 datanode 中。最后,namenode 注意到块备份数小于规定的备份数,他就安排在另一个节点上创建完成备份,直接从已有的块中复制就可以。然后一直到满足了备份数( dfs.replication )。如果有多个节点的写入失败了,如果满足了最小备份数的设置( dfs.namenode.repliction.min ),写入也将会成功,然后剩下的备份会被集群异步的执行备份,直到满足了备份数( dfs.replication )。
创建目录
文件压缩有两大好处:
Hadoop 对于压缩格式的是自动识别。如果我们压缩的文件有相应压缩格式的扩展名(比如 lzo,gz,bzip2 等)。Hadoop 会根据压缩格式的扩展名自动选择相对应的解码器来解压数据,此过程完全是 Hadoop 自动处理,我们只需要确保输入的压缩文件有扩展名。
Hadoop中有多种压缩格式、算法和工具,下图列出了常用的压缩方法。
表中的“是否可切分”表示对应的压缩算法是否支持切分,也就是说是否可以搜索数据流的任意位置并进一步往下读取数据,可切分的压缩格式尤其适合MapRece。
所有的压缩算法都需要权衡空间/时间:压缩和解压缩速度更快,其代价通常是只能节省少量的空间。不同的压缩工具有不同的特性:
更详细的比较如下
1.压缩性能比较
2.优缺点
另外使用hadoop原生(native)类库比其他java实现有更快的压缩和解压缩速度。特征比较如下:
使用容器文件格式结合压缩算法也能更好的提高效率。顺序文件、Arvo文件、ORCFiles、Parqurt文件同时支持压缩和切分。
压缩举例(Java)
压缩
解压缩
六、文件序列化
序列化是指将结构化数据转换为字节流以便在网络上传输或写到磁盘进行永久存储。反序列化狮子将字节流转换回结构化对象的逆过程。
序列化用于分布式数据处理的两大领域:进程间通信和永久存储。
对序列化的要求时是格式紧凑(高效使用存储空间)、快速(读写效率高)、可扩展(可以透明地读取老格式数据)且可以互操作(可以使用不同的语言读写数据)。
Hadoop使用的是自己的序列化格式 Writable ,它绝对紧凑、速度快,但不太容易用java以外的语言进行扩展或使用。
当然,用户也可以使用其他序列化框架或者自定义序列化方式,如 Avro 框架。
Hadoop内部还使用了 Apache Thrift 和 Protocal Buffers 来实现RPC和数据交换。