SpringBoot内置tomcat的ServletContext(。。。问题发⽣
近期,在线上有出现问题;截图如下:
仔细⼀看,是 / ;⼤家的第⼀反应是,呃,是不是 basedir 没有设置,跑到临时⽬录去了;所以,请运维同学 设置上basedir 的配置。之后过了N天,依然出现了这样的问题;
问题分析
注意,我们看看错误截图中的内容,是 /tmp/tomcat ;注意,是tomcat d ocbase ,⽽不是 tomcat ;tomcat d ocbase 是怎么出来的呢?是否和 tomcat.basedir 有关系呢?需要继续排查;
我们也检查了代码的位置,发现代码中,是将⽂件输出到了⼀个⽬录下;⽽这个⽬录的代码:
String outFilePath = ServletContext().getRealPath("/") + "upload/";
那么,是否: ServletContext().getRealPath("/") 这部分和basedir没有关系的呢?
servlet和tomcat的关系
tomcat的临时⽬录设置
⼤家都知道在springboot 1.x 中的临时⽬录设置⽅法;配置⽂件中加上⼀段:
也都知道,如果不设置这个的话,会在 pdir 环境变量⽬录下,出现⼀个port(服务端⼝号) 的⽬录作为临时 ⽬录;同时我们也都清楚,在linux 下 pdir ,如果没有特殊设置,默认是指向到: /tmp ⽬录下,⽽这个⽬录是在linux 下是 有定时任务 定期清理的;也就是说,如果我们不设置basedir ,那么真的是会出现以上的类似的错误,就是提醒:xxx ⽬录不存在之 类的;
⽽没有设置 basedir ,那么在 临时⽬录下出现的 tomcat ⽬录应该是:
tomat-docbase 和 basedir有关系吗?
看这部分,⾃然翻代码;springboot 是内嵌tomcat的;到 TomcatEmbeddedServletContainerFactory 这个类,这个是tomcat 内嵌的时候,相关的启动类;到 getEmbeddedServletContainer 的⽅法,如下:
这部分⽅法中,可以看到; 1)setBaseDir ,这个上下翻⼀下逻辑,是我们设置的 basedir ,那如果我们不设置 basedir,会⾃动创建⼀个 tomcat 开头的临时⽬ 录:createTempDir(String),这个⽅法,⼊参是:tomcat ;⽽createTempDir 如下所⽰:
在这⼉,我们可以确认,所谓的 tomcat ,不是我们的 basedir 没设置引起的;因为即使我们不设置,那么⽬录也不会 是tomcat d ocbase;
继续
接着,还是 TomcatEmbeddedServletContainerFactory ,我们继续;看到了 prepareContext 的⽅法;这个⽅法,是初始化 Context 的,是否和我们 要的 tomcat d ocbase有关系;代码如下:
确实是在这⾥了;翻了⼀下代码逻辑:getValidDocumentRoot(),在内嵌tomcat容器启动的时候,返回是空的;所以这部分的⽬录就 变成临时⽬录下 tomcat 的⽬录;
⽽ ServletContext().getRealpath(“/”),获取的就是这个⽬录;
⾃此,我们到了源头;
如何设置docbase
查看:getValidDocumentRoot⽅法:
实际上,在整个使⽤中,我们虽然到了 setDocumentRoot() ⽅法,但没有到调⽤⽅;因此默认的DocumentRoot也就⽆法设置, 当然我们可以⾃⼰重写这部分代码来实现;
docbase 的测试验证
1. 默认,仅设置base.dir
说明,baseDir 的设置,不影响docBase
2. 在部署⽬录中,增加了⼀个 public ⽂件夹
部署⽬录如下 :
输出如下:
3. 设置 pdir 变量
输出如下:
有个前提条件,上⾯第2个的 public ⽬录要删除掉;否则还会在那个public ⽬录下;
问题定位
由于我们没有设置 pdir 环境变量,⽽⼜使⽤了 ServletContext().getRealPath("/") 来获取⽬录;导致⽂ 件⽬录被定位到:/tmp/tomcat ⽬录下;
根据linux的规则,/tmp ⽬录下⽂件,会定期清理,导致⼀段时间之后,写 ⼊: ServletContext().getRealPath("/") ⽬录下,出现异常:⽂件或者⽬录不到;
之前⼀直认为的 basedir 参数设置,并不会影响到 ServletContext().getRealPath("/") 的取值,因此原来的解决 ⽅法并没有效果;
问题的解决⽅法
1. 最简单的⽅式 启动脚本加上对 pdir 的参数设置; 同样的,我们也知道 pdir ,在不设置 basedir 下的时候,也会调整basedir的⽬录;因此,可以通过pdir进⾏ 统⼀设置; 其实,在tomcat ⾃⾝的启动脚本中,也有类似的控制,如下:(tomcat bin\catalina.sh⽂件)
所以,可以在启动脚本中,获取到应⽤的当前⽬录,并将 pdir 设置到当前⽬录下的某个⽂件,例如:tmp;
2. 在部署⽬录下增加 public ⽂件夹 这种⽅式下,需要重新修改提供的部署包,暂时不推荐
3. 修改代码 由于是下载⽂件中,需要临时⽣成⼀个⽂件,⽽临时⽂件⽤了 ServletContext().getRealPath("/") 来获取⽬
录,导致了问题; 这部分代码修改掉,可以直接使⽤ ateTempFile() ,来创建临时⽂件;这样就可以完全避免上⾯的问题;当然这个只 是解决我们⽬前的这个问题;
tomcat的 docBase 、workDir 关系
如果我们将应⽤打包为 war 部署到tomcat 应⽤下的时候,最简单的⽅式是,将war包复制到 webapp ⽬录下,tomcat⾃⾏加载就可以了; 但其实,还有⼀种⽅式,就是通过 tomcat的 l ⽂件,⽽这个l⽂件中,是可以配置应⽤⽬录及上下⽂;
<Context docBase="D:\samples\WebRoot" path="/sample" reloadable="true" />
<!-- Context 表⽰运⾏在虚拟主机上的⼀个web应⽤程序,通常为WAR⽂件 -->
<!-- docBase 应⽤程序的路径或者是WAR⽂件存放的路径 -->
<!-- path 表⽰此web应⽤程序的url的前缀,就我们所说的请求上下⽂,这样请求的url为localhost:8080/**** -->
<!-- reloadable 这个属性⾮常重要,如果为true,则tomcat会⾃动检测应⽤程序的/WEB-INF/lib 和/WEB-INF/classes⽬录 的变化,⾃动装载应⽤程序,我们可以在不重起tomcat的情况下改变应⽤程序 -->
docBase 其实是⽤于设置我们的应⽤部署的⽬录;⽽内置的tomcat 容器,启动之后,也会⾃⾏创建⼀个这个⽬录(默认就在
pdir 指定的 tomcat 下);
workDir,是指tomcat 在运⾏中,会⽣成⼀些临时⽂件(⽐如 jsp 编译之后的 java / class ⽂件);⽽这些是放在了 workDir 下;如果是应⽤部署在tomcat 服务中,则在tomcat ⽬录下,有个:/work/Catalina/localhost ⽬录是⽤于保存这个;⽽在springboot 的内嵌tomcat 中,则是通过 basedir 来指定⽬录;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。