“遭遇黄牛党”一次系统优化实录

不知道从哪天开始,在开启 SchedulerJob(定时任务) 的情况下,产品环境的数据库连接在一天之内全被系统占满了(MySQL开了200个连接),只能重启数据库,重启服务。因为之前在系统中使用了C3p0数据库连接池,猜想可能是c3p0的配置问题或本省的问题?于是去掉数据库连接池,恢复到以前的设置,运行了一天,没有出现问题。没想到跑了两三天之后,后台直接死掉了,查看日志,是EntityManager nullpoint,似乎又是数据库连接拿不到?于是想到了以下的原因:

数据库连接不释放?

可能是运行后数据库连接不释放?长期累积导致数据库连接数耗尽?于是自己写了小测试来调用dao方法,做数据库操作。做了20000次顺序操作,没问题,数据库连接没有超过设定的连接池最大上限。做了3000次(3000个线程)并发操作,总体连接数没有超过数据库连接池的上限(maxPoolSize)上限。这就奇怪了,既然我压力测试dao接口,连接数没出现异常,那问题在哪里?那可能只是跑SchedulerJob的时候出现问题了?

内存占用过多?

将BatchJob的频率提高(每秒一次),使用YourKit对application进行监控,发现内存上升的非常快,没跑到一个小时就把设定的512M最大内存跑完了,请看下面两张图

image

Figure 1 在系统运行的短短一分钟之内,app就使用了近140M内存

image

Figure 2 在不到一个小时的时间之内占用了460M内存,接近设定的head最大值

查看系统Log ,发现系统在不断地产生新的Service类(假设为 SchedulerJobService ),怪不得系统内存消耗得特别快,那在Spring 配置文件中(applicationContext.xml)中将这个service的scope改为singleton不就好了吗?

产生了太多的Spring ApplicationContext(IoC Container)实例!

Spring reference 3. The IoC container

改之,以为万事大吉,再跑,发现还是不断地产生SchedulerJobService,难道scope改错了,查看文档,没错啊。彷徨之际只能再回过头看一下写的代码,我们用的是Quartz来做SchedulerJob的,在执行每一个Job的时候需要拿到 SchedulerJobService 的实例,这个实例当然就配置在Spring ApplicationContext里面啦。问题终于找到了!在拿SchedulerJobService这个Bean的时候我们是这么写的。

private
 ApplicationContext appContext = new
 ClassPathXmlApplicationContext("applicationContext.xml
");

每次都创建了一个ApplicationContext ,当然包括每一个Bean的实例化,Bean之间的依赖关系,以及创建一些数据库连接,这是在ApplicationContext中配置好的。

这就难怪了,不管我在ApplicationContext设置数据库最大连接数还是设置Scope都不管用了。因为现在面临的问题已经超出了单个ApplicationContext的管辖范围了。

假设SechdularJob是15分钟跑一次,那一个小时就会产生4个ApplicationContext, 一天就会产生96个ApplicationContext,即使我限制了在一个ApplicationContext中的最大连接数是10,可是一天下来产生的连接数可能有960之巨。在Mysql中默认的等待时间(wait_timeout)是8个小时,也就是说,一旦创建了一个连接,即使什么都没做,MYSQL也会在8个小时之后关闭这个连接。同时也解释了为什么内存消耗如此快的原因了。

wait_timeout, 详见MySQL system variable:wait_timeout

相关有意思的文章:MySQL wait_timeout default is set too high!

这就好像遇到了黄牛党,虽然每人限购两台,但是派了100 个黄牛过来,一下子就被抢购了200 台。

简单改之,变成静态变量

private
 static
 ApplicationContext appContext = new
 ClassPathXmlApplicationContext("applicationContext.xml
");

保证一个Job只有一个ApplicationContext。改完后测试,问题大大改善,见下面两图

image

Figure 3启动一分钟之后,内存使用相当平稳

image

Figure 4跑了40分钟不到,最大内存也只有120M

内存消耗比较稳定,没有明显的上升趋势。

进一步改进

刚才的那个方案只是保证了每一个Job只产生一个ApplicationContext,如果有两个SchedularJob的话还会产生两个ApplicationContext. 加上原有的一个ApplicationContext(随Web容器启动的),有好几个独立的ApplicationContext,其实他们之间完全可以共享一个ApplicationContext。所以接下来的改进步骤

1. 使用同一个ApplicationContext

2. Quartz使用Spring来管理

延伸阅读

· YourKit – Java Profiler (可选:jconsole, 特方便,JDK自带,但是比较迟钝,仅限不时之需)

· Quartz

· Spring 2.5 docs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值