hadoop豆知识

一些零散的知识点。

社区本来计划在2.2.0加入对symlink的支持,但发现各种问题推迟了。
2.2.0的代码:

DistributedFileSystem.java
12345678910
public void (final Path target, final Path link,      final boolean createParent) throws AccessControlException,      FileAlreadyExistsException, FileNotFoundException,      ParentNotDirectoryException, UnsupportedFileSystemException,      IOException {    if (!FileSystem.isSymlinksEnabled()) {      throw new UnsupportedOperationException("Symlinks not supported");    }    ......}
FileSystem.java
12345678
 public static boolean isSymlinksEnabled() {   if (conf == null) {     Configuration conf = new Configuration();          symlinkEnabled = conf.getBoolean("test.SymlinkEnabledForTesting", false);     }   return symlinkEnabled;}

相关jira:
https://issues.apache.org/jira/browse/HADOOP-10020
https://issues.apache.org/jira/browse/HADOOP-10019

一直到2.5.2,也还是不支持的。
2.5.2的代码:

FileSystem.java
1234567891011121314
private static boolean symlinksEnabled = false; 
private static Configuration conf = null; @VisibleForTesting
public static boolean areSymlinksEnabled() {  return symlinksEnabled;} @VisibleForTesting
public static void enableSymlinks() {  symlinksEnabled = true;}

CDH与官方版本的对应关系

CDH的版本有点混乱

CDH版本 官方版本
5.3.x、5.2.x 2.5.0
5.1.x、5.0.x 2.3.0
4.x 2.2.0

3.x版本不是基于hadoop2的,不列了。

log-aggression删除服务

发现一个奇怪的问题,log-aggression相关的服务是在NM里面的,见LogAggregationService类。
但删除过期日志的服务是在history server里面的,见AggregatedLogDeletionService类。
意味着如果不启动history server,则hdfs上的日志不会删除。
这个设计有点奇怪。
https://issues.apache.org/jira/browse/YARN-2985

2.5.2代码导入eclipse

前提:maven、protoc.exe 2.5.0、avro
按BUILDING.txt的说明:

1234567891011121314151617181920
cd hadoop-maven-plugins/mvn install
cd ..mvn eclipse:eclipse -DskipTests# 这时已经可以导入eclipse了,但有些类是proto和avro生成的,现在导入的话hadoop-common项目会有些error
cd hadoop-common-project/hadoop-common/src/test/protoprotoc test_rpc_service.proto --java_out=../javaprotoc test.proto --java_out=../java
cd ../avro# avro的jar可以从官网下载java -jar avro-tools-1.7.7.jar compile schema avroRecord.avsc ../java# 这时导入的话,hadoop streaming项目的classpath有些问题,要修改hadoop streaming项目的.classpath文档# 注释掉包含hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/conf的一行# 有什么副作用不知道# 在eclipse里导入existing project,选择root directory即可,eclipse会自动扫描所有项目并导入# 导入后classpath一般会有问题,找不到M2_REPO路径。不要用eclipse内置的maven,修改Windows->Preferences->Maven->Installations,改成自己的maven路径。# 之后所有项目都OK了,没有error,但有些warn,关系不大

补充:在JDK8的情况下导入步骤似乎又不太一样。环境:JDK8、macOS 10.12、hadoop-2.6.0-cdh5.6.1。

  • 需要修改根pom.xml中的<javaVersion>1.7</javaVersion>属性为1.8,否则maven-enforcer-plugin插件会报错。
  • 直接mvn eclipse:eclipse -DskipTests似乎有些问题,据说是eclipse插件的bug,需要用mvn org.apache.maven.plugins:maven-eclipse-plugin:2.6:eclipse -DskipTests
  • eclipse插件执行过程中出现了一个非常诡异的missing tools.jar错误,参考这个帖子解决即可。

导入eclipse后还是会报一些奇怪的error,有些error要手动修改build path引入jar包,有些error要手动修改项目编译级别到1.7(参考这个)。也可能是eclipse本身的bug。
不管怎样,最终完成后,所有error都可以消除。

顺便说下hive代码的导入。环境:JDK8、macOS 10.12、hive-1.1.0-cdh5.6.1。

参照这篇文章依次执行以下命令即可:

  • mvn clean install -DskipTests -Phadoop-2
  • mvn eclipse:clean
  • mvn eclipse:eclipse -DdownloadSources -DdownloadJavadocs -Phadoop-2(虽然我觉得这两个-D参数可以不要)

然后导入eclipse,也是会报一些奇怪的error,一般都是build path有问题。我碰到的是报一个scala-compiler-2.10.0.jar有错误,手动将有问题的项目的build path中的这个jar删除,就可以去除error。

如何开启客户端DEBUG日志

设置环境变量即可

1
export HADOOP_ROOT_LOGGER=DEBUG,console

这种方法适合临时debug。如果要长期开启DEBUG日志,可以将这个变量写到etc/hadoop/hadoop-env.sh中。
如果用的log4j.properties文档是hadoop自带的那个,还可以设置JVM属性-Dhadoop.root.logger=DEBUG,console。一样的效果。
因为log4j本身加载配置文档时支持对JVM变量的替换。

消除客户端的warn

客户端执行fs -ls /之类的命令时出现warn,不影响使用,但看着比较烦。
常出现的warn有两种:

1
14/11/10 12:18:18 WARN hdfs.BlockReaderLocal: The short-circuit local reads feature cannot be used because libhadoop cannot be loaded.

客户端完全不需要short-circuit read功能。在hdfs-site.xml注释掉short-circuit相关配置,warn就会消失。

1
14/11/10 12:18:16 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

hadoop客户端启动时会去java.library.path中尝试加载libhadoop,加载失败的话就会出现warn。具体为什么失败,要开debug日志看下,根据特定问题去找解决办法。

修改etc/hadoop/hadoop-env.sh,修改HADOOP_ROOT_LOGGER为DEBUG,console,再次运行命令观察输出。一个例子:

1234
14/11/10 14:53:42 DEBUG util.NativeCodeLoader: Trying to load the custom-built native-hadoop library...
14/11/10 14:53:42 DEBUG util.NativeCodeLoader: Failed to load native-hadoop with error: java.lang.UnsatisfiedLinkError: /home/mine/jxy/test/hadoop-2.2.0/lib/native/libhadoop.so.1.0.0: /lib/libc.so.6: version `GLIBC_2.12' not found (required by /home/mine/jxy/test/hadoop-2.2.0/lib/native/libhadoop.so.1.0.0)

14/11/10 14:53:42 DEBUG util.NativeCodeLoader: java.library.path=/home/mine/jxy/test/hadoop-2.2.0/lib/native
14/11/10 14:53:42 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

这个是由于编译时所用的glibc版本大于当前系统的glibc版本。解决方法:1.在当前系统下重新编译一次libhadoop;2.升级glibc到指定版本(未测试)。

如何查看hive-cli的日志

使用hive命令行时错误一般不会输出到stdout。而是写到文档/tmp/${user.name}/hive.log中。
另外,如果想开启DEBUG日志,可以修改$HIVE_HOME/conf/hive-log4j.properties将hive.root.logger改成DEBUG,console。
如果hive-log4j.properties不存在就将hive-log4j.properties.template重命名下。

相关配置:

1234567891011121314151617181920
<!--这是HDFS上的目录--><property>  <name>hive.exec.scratchdir</name>
  <value>/tmp/hive-${user.name}</value>
  <description>Scratch space for Hive jobs</description>
</property><!--下面两个目录是本地磁盘上的--><property>  <name>hive.querylog.location</name>
  <value>/tmp/${user.name}</value>
  <description>
    Location of Hive run time structured log file  </description>
</property><property>  <name>hive.exec.local.scratchdir</name>
  <value>/tmp/${user.name}</value>
  <description>Local scratch space for Hive jobs</description>
</property>

DN/NM挂掉后多长时间会标记为dead?

一个DN挂掉后,要过一段时间才会被判断为dead,在这段时间内NN还是会把这个DN返回给客户端,客户端尝试读取数据时就会报错。
于是想到一个问题,DN/NM挂掉后多长时间才会被认为dead?

对DN而言,有两个参数共同作用:dfs.heartbeat.interval(心跳间隔,默认3秒)、dfs.namenode.heartbeat.recheck-interval(默认5分钟)。
超时时间是2*dfs.namenode.heartbeat.recheck-interval + 10*dfs.heartbeat.interval。也就是说,默认超过10分30秒都没收到心跳,就认为DN挂掉了。
在我们的集群中,心跳是10秒,超时时间就是11分40秒。
这个逻辑见DatanodeManager类。

NM就简单很多了,心跳的超时时间是yarn.nm.liveness-monitor.expiry-interval-ms参数,默认10分钟。
这个逻辑见NMLivelinessMonitor类。
顺便说一句,NM的心跳间隔是yarn.resourcemanager.nodemanagers.heartbeat-interval-ms,默认1秒。不要随便增大NM的心跳,会降低集群的吞吐量,因为只有NM发来心跳时才能分配container。

在google的过程中,发现一个jira:HDFS-3703。社区对这种问题已经有了方案,当一个DN超过30秒没有发送心跳时,会被标记为stale状态,NN在向客户端返回时会将stale的节点放到最后一个,尽量减少出错的概率。但这个特性默认是关闭的。

关于HADOOP_CLASSPATH

仅针对MR而言。
对于MR任务需要关注两个classpath:1.客户端的classpath;2.NM端的classpath。
HADOOP_CLASSPATH变量就是用于设置客户端的classpath的,格式跟java -cp的一样:*。注意这里的jar包不会被传到NM节点。
要将相关的jar分发到所有节点,有几种方式

  1. 实现Tool接口。用-libjars参数分发。
  2. 在代码中手动调用DistributedCache的相关方法添加lib。
  3. 导出jar包时生成一个fat jar,将所有额外的lib放到jar包的lib目录里。hadoop jar xxx.jar命令会自动将jar解压,并将lib目录里的文档同时加入客户端和NM端的classpath。

最好保证分发的文档都是777权限,那样可以在不同用户间共享,减小上传文档的消耗。
以上3种方式其实都是同样的原理,实现方式不同而已,具体的逻辑去看NM的localize过程。

如何解决jar包冲突

接上条,如果自己用的jar和hadoop自带的冲突了如何解决?
对于客户端classpath,可以export HADOOP_USER_CLASSPATH_FIRST=true,会优先加载HADOOP_CLASSPATH中指定的jar。
对于NM端classpath,可以

12
// 用户自己的libjar优先于hadoop自带的conf.setBoolean(MRJobConfig.MAPREDUCE_JOB_USER_CLASSPATH_FIRST, true);

P.S.: JVM的类加载原则就是按classpath中出现的顺序加载。包名、类名都相同的话,后面的加载不会生效。

自定义JAVA_HOME

最近打算升级JDK7。
其实JDK的兼容性就一个原则:高版本的jdk可以运行低版本jdk编译的class文档,反过来不行。
如果服务端是JDK7,用hadoop jar执行一个JDK6编译的class,可以直接运行。
如果服务端是JDK6,但要执行一个JDK7编译的jar,可以手动设置JAVA_HOME,前提是服务端有对应的目录:

12345678910
<!--演示用,map用JDK6执行,reduce用JDK7执行,前提服务端必须存在对应的目录--><property>    <name>mapreduce.map.env</name>
    <value>JAVA_HOME=/usr/lib/jvm/java-6-sun</value>
</property><property>    <name>mapreduce.reduce.env</name>
    <value>JAVA_HOME=/home/hadoop/jdk1.7.0_75</value>
</property><!--还有一个属性yarn.app.mapreduce.am.env可以设置AM端的环境变量,一样的道理-->

除了JAVA_HOME,还有其他一些属性可以自定义,见yarn-default.xml中的yarn.nodemanager.env-whitelist属性。

xml配置文档中的坑

之前配置的snappy压缩没有生效,后来发现是core-site.xml中io.compression.codecs最后多一个换行。。。
这个属性是逗号分隔的,而SnappyCodec是最后一个,所以没生效。
注意所有xml配置项中都不要有换行,hadoop的Configuration类似乎没有做trim操作。

0.0.0.0

在hadoop的很多配置中,都要配一个网络地址,可能是RPC,可能是web。
我们一般是直接写域名,比如hadoop0.photo.163.org:8020
但最近碰到一些虚拟机有2个IP,客户端的IP和机房的IP不一样。而直接写域名的话,服务绑定到机房IP上,客户端无法访问。
这时就要写0.0.0.0:8020。0.0.0.0可以代表本机的所有IP地址。

2.2.0和2.5.2的container executor

升级过程中突然想到个问题,2.2.0和2.5.2的cotainer executor是否兼容?
实际测试是不兼容的:

12345678910
// 2.2.0hadoop@hadoop0:~/hadoop-current/bin$ ./container-executor Usage: container-executor --checksetupUsage: container-executor --mount-cgroups hierarchy controller=path...Usage: container-executor user command command-args// 2.5.2hadoop@hadoop40:~/hadoop-current/bin$ ./container-executor Usage: container-executor --checksetupUsage: container-executor --mount-cgroups hierarchy controller=path...Usage: container-executor user yarn-user command command-args

注意参数个数的变化。如果升级时不替换container executor,NM执行任务会报错:

12345678
2015-07-09 18:36:37,930 ERROR org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor: DeleteAsUser for /home/hadoop/disk/3/local/usercache/hadoop/appcache/application_1436437428207_0001 returned with exit code: 1
ExitCodeException exitCode=1: Too few arguments (5 vs 8) for initialize container        at org.apache.hadoop.util.Shell.runCommand(Shell.java:538)        at org.apache.hadoop.util.Shell.run(Shell.java:455)        at org.apache.hadoop.util.Shell$ShellCommandExecutor.execute(Shell.java:702)        at org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor.deleteAsUser(LinuxContainerExecutor.java:381)        at org.apache.hadoop.yarn.server.nodemanager.DeletionService$FileDeletionTask.run(DeletionService.java:263)

替换container executor后就正常了。NM进程不需要重启。
其实不光是container executor,升级过程中所有的native lib都应该替换。

jdk7编译的tar包是否可用于jdk6

升级2.5.2的过程中,我们也将服务端的jdk升级到7。在编译、打包的过程中,也是用的jdk7。
那这样编译出的tar包,是否可以用于jdk6?
答案是可以的,因为hadoop的pom文档中,限定了编译级别是1.6,即使是用jdk7编译:

hadoop-project/pom.xml
123456789
<plugin>  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>2.5.1</version>
  <configuration>
    <source>1.6</source>
    <target>1.6</target>
  </configuration>
</plugin>

用UltraEdit查看编译出来的class文档,major.minor版本也能证明确实是jdk6的class文档。

原文链接 大专栏  https://www.dazhuanlan.com/2019/08/17/5d576e200b379/

上一篇:功能地址在nm输出和gdb中不同


下一篇:字符串的操作方法