记一次jetty|记一次jetty 404问题排查修复

问题现象: 最近遇到一个很奇怪的线上问题:线上的管理后台web应用,经常在跑了一段时间后,访问出现404。
记一次jetty|记一次jetty 404问题排查修复
文章图片
image.png 之前查过好几次日志,并没有报错或是其他异常,重新部署后又能正常访问了。直到最近又出现了,遂决定彻底查一下原因。
观察到的现象是:
1.管理页面无法正常访问,显示404。如上图。
2.访问该应用的restful接口,可以正常返回。
记一次jetty|记一次jetty 404问题排查修复
文章图片
image.png 原因定位 通过现象可见,该应用并没有彻底down掉,因为接口还可以正常访问。所以排除了内存泄漏等导致应用挂掉的原因。
直观感觉是可能真的是找不到这个页面文件了,所以才会404。抱着这个思路试图找到jetty查找index.html文件的位置,即jetty将应用war包解压的位置。
查看jetty官方文档找到下面这一段:
By default, Jetty will create this directory inside the directory named by the java.io.tmpdir System property. You can instruct Jetty to use a different parent directory by setting the context attribute org.eclipse.jetty.webapp.basetempdir to the name of the desired parent directory. The directory named by this attribute must exist and be writeable.
翻译一下就是jetty默认使用java.io.tmpdir这一系统变量的路径作为解压路径。
可以使用如下命令查看系统属性:
java -XshowSettings:properties -version
部分输出如下:

# java -XshowSettings:properties -versionProperty settings:awt.toolkit = sun.awt.X11.XToolkitfile.encoding = UTF-8file.encoding.pkg = sun.iofile.separator = /java.awt.graphicsenv = sun.awt.X11GraphicsEnvironmentjava.awt.printerjob = sun.print.PSPrinterJobjava.class.path = .java.class.version = 52.0java.endorsed.dirs = /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.20-3.b26.el6.x86_64/jre/lib/endorsedjava.ext.dirs = /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.20-3.b26.el6.x86_64/jre/lib/ext/usr/java/packages/lib/extjava.home = /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.20-3.b26.el6.x86_64/jrejava.io.tmpdir = /tmp

或者在java代码中使用如下语句获取系统属性:
System.getProperty("java.io.tmpdir")
可以看到linux系统下默认为/tmp目录。遂去/tmp目录查看,并没有发现跟jetty解压相关的文件夹。猜想可能是/tmp目录定期清理时把相关的文件夹删掉了,导致解压出来的页面相关文件找不到。
查看文件tmpwatch查看清理周期:
vim /etc/cron.daily/tmpwatch#! /bin/shflags=-umc/usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \-x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \-X '/tmp/hsperfdata_*' 10d /tmp/usr/sbin/tmpwatch "$flags" 30d /var/tmpfor d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; doif [ -d "$d" ]; then/usr/sbin/tmpwatch "$flags" -f 30d "$d"fidone

可以看出来清理周期为30天,基本跟问题复现的周期吻合。
解决问题 找到问题了,来看怎么解决。jetty官方文档给出几种设置解压路径的方法:
通过xml文件配置:
/testfoo.warorg.eclipse.jetty.webapp.basetempdir/home/my/foo通过java代码配置:WebAppContext context = new WebAppContext(); context.setContextPath("/test"); context.setWar("foo.war"); context.setAttribute("org.eclipse.jetty.webapp.basetempdir", "/tmp/foo");

或者还有一种最简单的方法,在jetty安装的根目录下建一个叫work的文件夹,jetty就会使用该文件夹作为解压的路径了。我使用的是这种方法。
Mostly for backward compatibility, from Jetty 9.1.1 onwards, it is possible to create a directory named "work" in the ${jetty.base} directory. If such a directory is found, it is assumed you want to use it as the parent directory for all of the temporary directories of the webapps in ${jetty.base}.
记一次jetty|记一次jetty 404问题排查修复
文章图片
image.png 记一次jetty|记一次jetty 404问题排查修复
文章图片
image.png 新建完work目录后,重新部署应用,会发现该目录下jetty自动创建的解压文件夹,文件夹下有页面相关的文件。
到此,问题的原因和解决方案都有了,问题也算解决了。
更进一步 仔细想一想,还有一个问题没有解决。为什么应用解压的文件夹被删除,页面文件肯定找不到了,但是跟页面无关的接口却正常工作呢?
是否是jetty把静态资源放在上述的解压文件夹下,class等文件放在别的地方了呢。
我们都知道,jetty和tomcat要部署war包都只要把war包放在根目录的webapps目录下。查看原始的war包文件,内容如下。
记一次jetty|记一次jetty 404问题排查修复
文章图片
image.png 可以看到war包原始内容跟work下解压出的内容完全一致。其中WEB-INF目录下包括了class、lib引入的jar包等文件夹,符合servlet规范要求。
1)/WEB-INF/
专门的Servlet API定义文件夹,通常存储和Web应用相关但不为外部访问的任何东西。
如果你有内容被你的Web应用内部访问,但不会被web浏览器直接地访问,你就应该把他们放在这里。
2)/WEB-INF/web.xml
必须的部署描述符,用于定义你的Web应用的各种行为。
3)/WEB-INF/classes/
Web应用的java classes文件放置目录。
4)/WEB-INF/lib/
JAR文件放置的目录。
记一次jetty|记一次jetty 404问题排查修复
文章图片
image.png 此时尝试把work目录下解压出来的文件夹删除,依然是页面404无法访问,接口可以。
继续查看jetty部署相关章节文档,并没有找到相关的描述,只能大胆的猜测:
1.class文件、jar包等内容,jetty是在解压后就加在到内存中的,jvm在运行时,并不会访问之前解压出来的文件,所以删除没有影响。
2.html、css等静态资源,当有页面访问时,是从之前解压的文件夹中读取的,所以删除会报404异常。
【记一次jetty|记一次jetty 404问题排查修复】如有不同看法,欢迎指正。
参考:
http://www.eclipse.org/jetty/documentation/current/ref-temporary-directories.html
http://www.eclipse.org/jetty/documentation/current/configuring-deployment.html
http://blog.csdn.net/u011479200/article/details/79200788
https://stackoverflow.com/questions/26459904/reading-java-system-properties-from-command-line

    推荐阅读