一次由Tomcat重新加载引发的血案

引言: Tomcat是目前开发中使用非常广泛的Web服务器,其提供了很多优秀和简便易用的功能。我们这里的血案就是由于在部署过程中,不慎而引发的....

1. 问题的出现

在我们的系统后台每日定义会触发一些任务操作,进行一些数据的处理和更新,从上线到现在已经正常运行了将近3个多月了。忽然某一天,发现其数据不对,大量的数据都是被执行了2次某个定时任务A造成的。 查询日志,的确是执行了2次。但是之前为什么是正常的呢?诡异诡异....

环境: Tomcat 6.0.x

2.  血案的发生

由于在生产系统中,数据出现了问题,这个是一个非常严重的事情。于是首先把这个定时任务A先停掉,清理数据。由于数据量比较大,所以就有了焦头烂额的几天修正系统数据的过程。

3.  问题分析

3.1  任务A本身有问题吗?

之前都是正常运行的, 只在最近几天才发生问题。而且基于日志分析,其数据操作是正确的,问题是操作了2遍。

3.2  任务A实现分析

首先怀疑其实现的代码上是否有问题? 经过分析其是基于Quartz来实现定时任务的,Quartz是老牌的定时框架,不会出现问题。 是否是我们自己使用的方式问题,并只在特定情况下才会出现问题呢? 经过代码的排查分析,没有问题。

3.3  代码变更记录分析

是否是某个同事无意之前做的什么修改造成的呢? 经过针对代码变更记录和上线记录的分析,排除这个可能。

4.  系统日志分析

在排除了这个任务A自身问题之后,将目光转移到了环境本身。  部署环境是Tomcat6.  下载了Tomcat日志文件进行分析,逐渐找到了某些蛛丝马迹,从而剥丝抽茧般的发现了问题的原因。

在系统日志中,发现了若干在应用部署过程中的错误信息,这个引起了我的怀疑。是否是由于在部署中的错误造成的呢?

经过排查,确定是部署不慎造成的,而且是由于Tomcat本身的Reloadable特性造成的问题。

5.  问题场景的还原

1.  某位同事由于在修改某个问题,只修改了某几个Class文件,故其将编译后的class文件在服务器上直接进行了替换。

2.   由于Tomcat本身设置了Reloadable的属性,故Tomcat在检测到class文件发生变化之后,对WebApp的应用进行了重新加载:先卸载,再重新加载。

3.   在卸载的过程中,程序出错,卸载失败,原来的应用没有卸载成功,保留在Tomcat的缓存之中。

4.   虽然卸载失败,Tomcat继续加载新的WebApp应用。这里请注意: 新加载的WebApp应用在项目名称之后加了一个2,比如原来的项目名称是AAA, 新的项目名称就是AAA2。 重新定义了一个项目名称哦,我当时就知道,这里就是问题所在。

此时,二次加载的Web应用,也由于某些原因失败了。

5.  运维的同事,注意到了这些错误信息,由于重新启动了Tomcat,这次Tomcat正常启动了

第一个Web应用正常启动了

第二个Web应用也正常启动了,参看日志。

6.  于是在我们的Tomcat中就出现了两个一模一样的Web应用,换句话说,就是出现了两个一模一样的定时任务。 这个就是为什么定时任务会被执行2次的原因。

7. 解决以及应对

7.1 不允许动态的在生产系统中,替换class文件

7.2  在系统部署和上线过程中,需要进行日志分析,日志可以告诉我们一切的技术细节和内容。

7.3  将Tomcat的Reloadable设置置为false

<Context path="/helloApp" docBase="helloApp" debug="0" reloadable="false"/>

8.  reloadable参数说明

Context>代表了运行在<Host>上的单个Web应用,一个<Host>可以有多个<Context>元素,每个Web应用必须有唯一的URL路径,这个URL路径在<Context>中的属性path中设定。

    <Context path="/helloApp" docBase="helloApp" debug="0" reloadable="true"/>  

<Context>元素的属性:

path:指定访问该Web应用的URL入口。

docBase:指定Web应用的文件路径,可以给定绝对路径,也可以给定相对于<Host>的appBase属性的相对路径,如果Web应用采用开放目录结构,则指定Web应用的根目录,如果Web应用是个war文件,则指定war文件的路径。

reloadable:如果这个属性设为true,tomcat服务器在运行状态下会监视在WEB-INF/classes和WEB-INF/lib目录下class文件的改动,如果监测到有class文件被更新的,服务器会自动重新加载Web应用。

在开发阶段将reloadable属性设为true,有助于调试servlet和其它的class文件,但这样用加重服务器运行负荷,建议在Web应用的发存阶段将reloadable设为false。

9.  总结

由于一个微小的变化引发的问题,在这之前一直正常。由于动态替代class,引发自动部署;部署失败,导致出现了2个相同的部署包。再次重启,然后2个缓存包都正常启动。 其本质原因是Tomcat在卸载应用的过程中,如果出现异常,则无法继续删除缓存;在重新加载的时候,无法清空已有的缓存,这个才是问题的根源。

我们姑且把这个问题,作为Tomcat的一个bug吧,只在特定情况下才会出现的问题。

时间: 2024-08-29 18:47:26

一次由Tomcat重新加载引发的血案的相关文章

tomcat 无法加载js和css 等静态文件的问题

前段时间做了个网站,在本地tomcat测试都没有问题,但是部署到阿里云上之后,系统样式全没了.jsp等动态页面访问正常. 打开浏览器监控发现所有的css 和js 文件返回都是404 .直接访问单个的css 文件也同样是404.以为是路径错了,于是一一对了一遍各个文件的路径和服务器上 tomcat路径方面的设置 发现路径没有问题.在网站更目录创建一个css文件和html文件均不能访问.确认不是网站路径的问题. 然后怀疑是tomcat 和 Apache 冲突了,或者可能是Apache 的设置不对.因

关于Tomcat自动加载更新class的小技巧

在Tomcat的server.xml或者Tomcat.xxx\conf\Catalina\localhost\project.xml文件中里Context标签内:<!-- reloadable为fase表示项目不自动布署 --> <Context reloadable="false"> <!-- reloadable为true表示项目自动布署 --> <Context reloadable="true"> 第一步:将r

[Eclipse] - 集成Tomcat热加载插件

使用Eclipse + Tomcat,要使用热加载,总是会重启tomcat webapp. 可以使用这个插件:jrebel 如果是Tomcat 7.0+版本,需要使用jrebel5.5.1+的版本,不然总会报一些错误. 使用方法: 1) 下载jrebel 5.5.1+ 2) 右键项目,选择: 3) 找到"VM arguments": 里头填入: -Dcatalina.base="E:\JavaProject\.metadata\.plugins\org.eclipse.wst

tomcat 热加载

近期在servlet开发.由于servlet在tomcat中的生命周期问题(请查阅其他文档),导致调试过程很不方便. 修改后的文件,无法在servlet中重新加载(重启tomcat当然可以,效率是个问题),解决方法是采用 热部署 和 热加载,两者在调试过程中各有自己的优缺点. 此处以热加载,描述下tomcat中的配置. 修改文件:apache-tomcat/conf/server.xml 修改内容:标签中添加 如下 标签 以下是我的配置: <Context debug="0" p

apache代理tomcat无法加载css,js,图片等资源的解决方法

这个问题苦恼了很久,应该就是开发的代码获取项目的相对路径和绝对路径的问题,但是不改代码用apache代理解决能否实现呢?答案是可以的. 下面是apache代理tomcat访问并https访问的配置 apache2.2,yum安装的 tomcat7,jdk1.7 tomcat的server.conf配置 在Host里加上Context <Host name="localhost"  appBase="webapps" unpackWARs="true&

eclipse中tomcat无法加载spring boot

转自: http://blog.csdn.net/u010797575/article/details/50517777 最近搭建一套spring boot框架,作为 application 启动项目OK, 但将 spring boot web放入 自己配置的tomcat容器中,出现不加载spring boot,直接启动成功. 这可能是因为项目引用 spring-web 失败.解决方案 1.首先检查项目的环境时候有问题(eg:build path/ java compiler/ project

Tomcat网页加载速度过慢的解决方法

可以vim conf/server.xml,在# OS specific support.  $var _must_ be set to either true or false.下添加: JAVA_OPTS="-server -Xms1024m -Xmx6144m -Xss1024K" 这段命令主要是用来设置Tomcat运行内存大小,Tomcat是内存大户,根据项目需求具体设定. vim server.xml <Connector port="8080" p

tomcat启动加载

Listener的初始化最早,Filter次之.他俩的初始化都是在容器启动完成之前初始化的. Servlet没有初始化,原因是没有匹配的请求进来. 如果想要servlet自动初始化,需要在指定的servlet中配置<load-on-startup>参数,没有此标签,默认启动时servlet不进行初始化. 初始化的顺序跟Listener.Filter.Servlet在web.xml中的顺序无关 而多个Filter或多个Servlet的时候,谁的mapping在前面,谁先初始化. 如果web.xm

SpringBoot项目改成Tomcat容器加载(打war包)

废话不多说,直接上码 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/PO