引言:为什么prefill=false会在启动的时候碰到连接风暴,本节结合JBOSS源代码的来分析这种情况产生的根本原因,最后给出几种方案来解决这种连接数突然飙升的问题。(prefill这个参数在jboss4.0.5版本以后才能够被支持)
连接风暴,听起来这个词很时髦,那么到底什么是连接风暴?在百度和google上搜索了下,都没有找到相关的解释。所谓连接风暴,我们也可以称之为访问风暴,这是在使用jboss默认参数prefill=false的情况下,大规模应用集群很容易碰到的问题。先来描述一个场景:
在项目发布的过程中,我们需要重启应用,当应用启动的时候,经常会碰到各应用服务器的连接数异常飙升。假设连接数的设置为:min值3,max值10。正常的业务使用连接数在5个左右,当重启应用时,各应用连接数可能会飙升到10个,瞬间甚至还有可能部分应用会报取不到连接。启动完成后接下来的时间内,连接开始慢慢返回到业务的正常值。这种场景,就是碰到了所谓的连接风暴。
知道了连接风暴的场景,那么它到底有什么危害呢,简单来说产要有以下几点(这些都是实际碰到的情况):
1. 在多个应用系统同时启动时,系统大量占用数据库连接资源,可能导致数据库连接数耗尽。
2. 数据库创建连接的能力是有限的,并且是非常耗时和消耗CPU等资源的,突然大量请求落到数据库上,极端情况下可能导致数据库异常crash。
3. 对于应用系统来说,多一个连接也就多占用一点资源。在启动的时候,连接会填充到max值,并有可能导致瞬间业务请求失败。
为什么我们的应用系统会在启动时会产生连接风暴。前面第二节我们已经介绍了jboss在启动的时候,有一个参数 < prefill >,这个参数决定了启动时是否初始化数据库的连接,默认设置为false,即启动时不初始化min值连接数。min值的初始化在业务请求调用的第一次getConnection时进行。
下面来看一下,getConnection的基本流程:(详细细节参考:http://www.dbafree.net/?p=378)
假设min值:9,max值:20,我们结合上图来分析一下应用启动时的场景。因为数据库创建一个连接需要耗费的时间在80-100ms。在数据库启动的时候,80ms之内,如果发生了并发的10个getConnection操作(假设这10个getConnection都不能被复用),则会怎么样呢?一共会创建多少个连接呢?
在80ms之内,第一个并发getConnection会执行filltoMin(创建9个连接),其它的9个并发请求会各自独立的创建一个连接,所以一共会创建9+9=18个连接。这个过程的流程图如下:
在实际执行filltoMin操作需要9*80ms=720ms左右,即在80ms,160ms,240ms的时候,会分别创建1个连接,创建的连接,马上就可以被使用。所以,连接风暴会在最初的80ms内非常明显。
当这18个连接被创建后,我们前面在章节 JBOSS连接池的初始化及关闭中已经提到,连接池初始化的时候会把当前连接池注册到IdleRemove线程中,对于空闲30分钟以上的连接会进行定时清理,这个定时任务每隔15分钟运行一次。所以定时任务执行时,会清理空闲时间在30分钟-45分钟的空闲连接。因此,在最多45分钟之后,连接又可以回落到正常的业务值。
了解了连接风暴的原理后,我们可以思考一下解决方案。这是目前总结的三种方案:
1. 设置jboss连接池参数prefill=true,即在启动时将连接数填充到min值。
2. 不设置prefill,在启动完成,业务正常运行,应用先执行一个SQL(如oracle可以执行:select * from dual),将连接数填充到min值。
3. 选择在业务低峰时重启系统。
以上三种方案中,第一种是最简单的,也不需要任何的修改。第二种方案有点麻烦,实现的方式和第一种方案本质上一样,进行连接预加载。我碰到过有的系统实现了这种方案,并且预加载了PreparedStatementCache。当然,我觉得意义并不是很大,不知道为什么会有人选择这种方式来实现,只能猜想也许是程序员并不知道第一种方式的存在。第三种方案,不用说了,治标不治本。
总结三种方案,我们可以通过简单的设置prefill=true来解决连接风暴的问题。
以上为个人见解,欢迎大家进行补充。
发表评论