背景
有一个C++项目会调用Hadoop,该项目在部署时会利用脚本来配置所需要的CLASSPATH和其他环境变量。
在启动时发现报如下错误:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/hadoop/conf/Configuration
Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.conf.Configuration
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
显而易见,是类的包找不到,通常状况就是CLASSPATH配置错误,或者真的缺失该包。
问题排查
首先排除了classpath不存在,和包不在的情况。通过export命令,查看环境变量,发现打印出的classpath也没有问题。
最后用gdb单步调试,去看到底哪里出了问题,调试时发现打印的CLASSPATH路径如下:
$3 = 0x4aa6000 "/thirdparty/hadoop/hadoop-2-core.jar:\033[0m\033[0m/thirdparty/hadoop/lib/abaci-core-3.29.0.jar\033[0m:\033[0m/thirdparty/hadoop/lib/ant-1.8.1.jar\033[0m:\033[0m/thirdparty/hadoop/"...
可以看到里面有\033[0m\033[0m 这种奇怪的字符,猜测就是它导致了classpath不正确的问题。
单这个特殊字符串是什么意思呢?通过Google发现,是linux中为了让shell输出的字体带颜色的设置。
那为何会引入这些控制字符呢?我们发现是因为该classpath是由一个脚本设置的,如下:
for f in `ls ${HADOOP_HOME}/lib/*.jar`; do
CLASSPATH=${CLASSPATH}:$f
done
export CLASSPATH
脚本中会用到ls逐个取每个包的名字,而查看~/.bashrc的设置,正好有一个
alias ls='ls --color'
于是真相大白,就是这个参数引入了颜色控制字符。而颜色配置信息,在shell中进行export时正好看不到,这就是为何export时看到的路径是正确的,但是程序通过
getenv("CLASSPATH");
获取到的环境变量就不正确的原因。
问题解决
去掉bashrc中的ls特殊配置即可。
对于线上部署的要控制好环境变量,不要修改常用的shell命令,当在别人的机器上部署出现类似问题时,不妨看一下是不是这种问题导致的。