在工作中碰到一个问题,从运维管理平台向交易平台发起重新加载服务请求,服务重新加载后再去做交易,而刚修改的限额、限次等配置信息没有生效。
以下是该问题相关情况:
1.本地代码运行没有该问题,部署到服务器上就有问题。
2.本地代码启动过程中服务只起了一次,而服务器上启动时起了两次;
3.远程调试服务器上代码,发现重新加载服务时,更新的限额限次配置和重新进行交易时使用的限额、限次配置不是同一个变量(变量的id不一致)。
4.限额限次配置对应的存储变量使用的是volatile修饰符修饰的。从而实现多线程同步的功能。
分析:
1.最开始怀疑是volatile变量的问题。但本地代码单步调试没有问题。 查阅volatile相关资料,发现其含义、用法都没有问题。 新配置生效时是新new了一个变量,然后替换原变量。故此volatile用法没问题。
2.本地代码运行没问题,服务器运行有问题。怀疑是服务器环境问题。但不确定哪里有问题。所以使用了远程调试。无意中发现,更新后的限额限次配置对应的变量和重新加载后使用到的限额限次变量id不一致。说明用的变量根本不是同一个变量。而结合以前忽略掉的一个问题现象:服务器上启动服务时,服务起了两遍。 所以认为服务重载无效问题和服务器上服务启动多次问题属于同一个问题。
3.服务启动是在servlet的init方法中启动的,每次服务启动第二遍时,前面的打印都是filter配置成功。怀疑与filter有关。也怀疑servlet的bean生成时,由于某种原因init方法执行了两遍。故此将服务启动改成再listener的init方法中启动。 但依旧是启动两遍。
4.上网查阅contextloadlistener启动两遍的文章,发现有一种说法是server.xml中配置不当造成。 打开自己服务器上server.xml发现,发现有个性化配置。而本地服务器下面的server.xml没有进行该修改。感觉问题终于找到了。这也解释了为什么本地服务没有问题,而服务器上服务却有问题。
server.xml中做的修改是,在Host标签中,增加了一行context,而Host中的appBase=webapps没有改变。这样tomcat会先启动webapps下面的所有项目(这是第一次启动服务),再启动配置的context(这是第二次启动服务)。故此启动了两次服务。 在加context之后,要将appBase的值设为空,这样便只会加载context中配置的项目了。
修改前server.xml部分截图如下:
加入context配置后的正确配置截图如下:
深入分析:
查阅相关资料,了解context的使用方法。 context主要是进行了一个虚拟映射,像上面配置的那样,做过此映射后,访问项目时,原先需要输入ip:port/projectname/xxxx,现在只需要输入op:port/xxxx即可。
此外,此context配置会导致war包解压后,解压到了catalina_path下面,就是和bin同级的目录中,且文件夹名字是ROOT。
如果bin同级的目录下也有一份项目的文件夹,那么启动时依旧会启动两次服务,因为启动了两个项目。(一个是context中配置的,一个是catalina_path下面的哪个项目文件夹).
如何正确配置,使得只启动一个项目需要进一步考虑。 由于时间关系,暂时没有深入研究。
如果像上面那样配置了一个context后,再配置一个<Context path="/pay-core-baixin" docBase="webapps/pay-core-baixin" debug="0" reloadable="true" />.
那么此时启动服务,就会启动两遍服务。因为有两个context。但访问路径不一样,一个是不用带项目名称,一个是需要带项目名称。但这两个项目各自独立,互不影响,只要重新加载服务和新交易使用的都是同一个项目服务,那么就不会出问题。
回头想想以前的配置情况,也是启动了两个项目,为什么重新加载服务和新交易使用的服务就是两个项目呢?重新核对重新加载服务和新交易使用的url,发现由于历史原因,这两套url不是同一套,这就导致了各自操作的项目不是同一个项目,从而出现重新加载服务无效的问题。
总之,起两个项目是不对的,但如果所有交易/访问都针对这两个项目中的同一个项目,也不会出问题。怕就怕,起了两个项目,所做交易/访问又不是同一个项目,这样问题就产生了。
教训:1.分析问题时,当前状况要了解清楚。比如该问题中服务器上server.xml做了个性化配置这事不知道。
2.要进行对比分析,本地和服务器,各个相关的配置文件都进行对比分析。
3.要多上网搜相关的文章,根据问题现象搜。