描述
在本地运行的项目测试通过。可是部署到linux服务器后,发现测试出现些问题。服务器环境是使用docker安装的tomcat。
进行
一般来说,出现问题,排查日志能解决大部分情况。可是docker进入运行中的tomcat,发现logs目录下并没有网上说的/usr/local/tocmat/logs/catalina.out文件。在进入到l/usr/local/tocmat/logs目录下,发现其文件结构如下。
catalina.2020-04-03.log host-manager.2020-04-03.log localhost_access_log.2020-04-03.txt
localhost.2020-04-03.log manager.2020-04-03.log。
为了再确认下,在tomcat安装目录下使用了如下命令找了找,发现还是没。
find . -name "*catalina*"
这个文件比较关键,因为里面记录了java程序的控制台输出和异常信息。对于此在网上找了很多解决方案,最后一个说法非常适用。参考文章地址在文末。
原因
首先,打开/usr/local/bin/catalina.sh文件,看其中关键的部分如下。
从图中可以看到,当使用/usr/local/tocmat/catalina.sh run的时候,其实是不会创建/usr/local/tocmat.logs/catalina.out文件的,因为这个文件只有在catalina.sh start的时候才会创建。而docker的初始执行命令就是sh /usr/local/tocmat/bin/catalina.sh run。因此需要覆盖这个初始命令。
catalina.sh部分如下。
elif [ "$1" = "start" ] ; then
if [ ! -z "$CATALINA_PID" ]; then
if [ -f "$CATALINA_PID" ]; then
if [ -s "$CATALINA_PID" ]; then
echo "Existing PID file found during start."
if [ -r "$CATALINA_PID" ]; then
PID=`cat "$CATALINA_PID"`
ps -p $PID >/dev/null 2>&1
if [ $? -eq 0 ] ; then
echo "Tomcat appears to still be running with PID $PID. Start aborted."
echo "If the following process is not a Tomcat process, remove the PID file and try again:"
ps -f -p $PID
exit 1
else
echo "Removing/clearing stale PID file."
rm -f "$CATALINA_PID" >/dev/null 2>&1
if [ $? != 0 ]; then
if [ -w "$CATALINA_PID" ]; then
cat /dev/null > "$CATALINA_PID"
else
echo "Unable to remove or clear stale PID file. Start aborted."
exit 1
fi
fi
fi
else
echo "Unable to read PID file. Start aborted."
exit 1
fi
else
rm -f "$CATALINA_PID" >/dev/null 2>&1
if [ $? != 0 ]; then
if [ ! -w "$CATALINA_PID" ]; then
echo "Unable to remove or write to empty PID file. Start aborted."
exit 1
fi
fi
fi
fi
fi
shift
touch "$CATALINA_OUT"
if [ "$1" = "-security" ] ; then
if [ $have_tty -eq 1 ]; then
echo "Using Security Manager"
fi
shift
eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \
-D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
-classpath "\"$CLASSPATH\"" \
-Djava.security.manager \
-Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" start \
>> "$CATALINA_OUT" 2>&1 "&"
else
eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \
-D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
-classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" start \
>> "$CATALINA_OUT" 2>&1 "&"
fi
if [ ! -z "$CATALINA_PID" ]; then
echo $! > "$CATALINA_PID"
fi
echo "Tomcat started."
对于新的覆盖命令。有两种解决方案,一种/usr/local/tocmat/bin/catalina.sh start这个命令,可是这个执行后,一会tomcat就停止了(去掉59行和69行最后的"&", 参考文章见文末)。另外一种,是将/usr/local/tocmat/bin/catalina.sh run的输出重定向到/usr/local/tocmat/bin/logs/catalina.out中。 我们采用第二种方式。该方式的命令如下。
/usr/local/tomcat/bin/catalina.sh run >> /usr/local/tomcat/logs/catalina.out 2>&
将这个命令作为一个脚本, 名称为/usr/local/tocmat/bin/start.sh 挂载到docker容器中,然后用sh /usr/local/tocmat/bin/start.sh覆盖初始命令。
最终的docker启动tomcat容器命令如下。
docker run --name tomcat -p 8080:8080 -v ~/docker/mount/tomcat/webapps:/usr/local/tomcat/webapps -v ~/docker/mount/tomcat/bin/start.sh:/usr/local/tomcat/bin/start.sh -d tomcat bash /usr/local/tomcat/bin/start.sh
扩展
使用上面方法,可以在/usr/local/tocmat/logs/目录下创建出catalina.out文件。通过日志排查,发现在windows上传文件的路径解析这个路径的文件名时候,发生异常。定位到当时上传文件的文件名为D:\temp\substrate.txt, 在linux上获取文件名的时候还是D:\temp\substrate.txt。
打开getName的源码如下。
public String getName() {
int index = path.lastIndexOf(separatorChar);
if (index < prefixLength) return path.substring(prefixLength);
return path.substring(index + 1);
}
如图所示,getName是和文件系统的分隔符(separatorChar)有关。对此我们做了下面测试。在同一个代码,分别在windows环境和linux环境进行测试。
String origin_file = "D:\\abc\\def\\k.txt";
System.out.println(new File(origin_file).getName());
上面代码在windows上输出是k.txt, 而在linux上输出却是D:\abc\def\k.txt。
解决
对于上面情况,一般需要编写可以跨平台的代码。其一是使用File.separator分隔符来代替具体的"\“或者”/"。其二对于windows系统来说,"\“或者”/”都可以,对于linux系统来说,只能使用"/",因此可以将路径全部写成"/"。
参考
https://www.cnblogs.com/barneywill/p/11013669.html
https://www.cnblogs.com/kangping/p/7150881.html