该文描述了解决问题的过程
大概描述的是PooledConnectionFactory连接池 连接大面积释放时造成系统拥堵的解决方案
如仅关心解决方案请拖到最下方即可
涉及:
JAVA + Spring + ActiveMQ + PooledConnectionFactory
前情提要:
随着业务的增长 在一个月黑风高的晚上 由于MQ获取不到连接 导致平台无法正常运行 于是乎修改了最大连接数(maxConnections=10000)
发现问题:
一直以为MQ的问题得以解决,直到发现平台频率性的异常,最终把目光又投向了MQ。
通过日志打印连接数,发现每分钟获取近150连接,盯着它盯了1个小时多一点,发现连接数到达之前设置的maxConnections=10000,这个时候开始发现连接以200多个的速度开始释放,此时平台频率性的异常出现了,直到连接重新到达0时MQ才重新恢复工作。
该释放连接的过程一条异常日志都没有。这才是最气的!!!
搜索解决方案:
不知道是我关键字没输入对还是咋的,查了一波都是教如何使用PooledConnectionFactory亦或者是改用spring的。Emm 我不想改线上代码,我觉得改了代码又得整天盯着它有没有问题。
那竟然它到了maxConnections就开始释放,那第一时间想到肯定是因为之前连接没释放所以导致最后一次性释放导致平台阻塞近50秒。
搜到的就几个参数 IdleTimeout 、 ExpiryTimeout 。
idleTimeout:空闲的连接过期时间,默认为30秒 即 连接开始空闲的时间戳 + idleTimeOut < 当前时间 即认为失效
expiryTimeout : 连接过期时间,默认为0 和上面不一样的是连接被创建开始 + expiryTimeout < 当前时间 即认为失效 0应该是不失效吧,没细看
于是乎就针对这两个参数,填了两个数字上去(别问我填了啥,因为最终是没效果,所以我就不说了)
最终结果就是又盯了1个多小时 开始大面积回收连接 就是这个结果并没有发生改变,连接只增不减,到达最大值再给你一次性减了。
看源码:
synchronized是阻塞的关键
如果获取连接数小于最大连接数(getConnectionsPool().getNumIdle(key) < getMaxConnections())则正常获取连接,否则循环获取可用连接。
挺正常的代码,重点就在borrowObject 在里面代码循环连接,如果判断连接失效则调用destroy
此处问题就是,你设置的失效参数是当所有连接拿了一轮之后(即1万个连接都用了后)才判断前面的连接是否失效。那当然失效了呀,1万个连接一轮共花1个多小时,而失效时间是30秒,所以在到达1万连接后开始大面积释放(destroy),并且synchronized让后面的连接只能等待释放完成后再获取。
再次查找解决方案:
仔细品了这篇文章后https://blog.csdn.net/u013760858/article/details/49664449问题得以解决。别问我为啥之前没搜索到,因为这文章文字密密麻麻的,哈哈 但是最后看完之后受益匪浅。
大概意思讲的是
里面维护了一个LinkedList,每次获取链接都从list取出ActivemqConnection给你使用并放在list的最后,通过这个方式做简单的负载均衡。
所以当获取链接一轮之后其实是可以再次使用之前的ActivemqConnection,而且ActivemqConnection是可以被多条线程同时使用的,只是创建session的是一个线程只会有一个。
那这就很明确了,之前的想法是希望线程提前释放,那现在应该是想办法不要让他释放。
解决方案:
通过上面的分析,得出的办法就是尽可能让连接在失效前进入下一轮使用,就是想办法不要让他释放。
当正常使用mq时算的应该是session数而不是connection(连接)数, 默认值下 session数是500,即1条连接可以保证500条线程工作。
如果业务量大 且服务器支持的前提下则将maxConnections设置大,并且将idleTimeout失效时间设置长一点,他两的关系就是在idleTimeOut时间内业务必须将maxConnection都创建完并使用一轮
例如: 平台每分钟150条连接 那maxConnection<150, idleTimeOut=60000
maximumActiveSessionPerConnection,该参数设置一个连接最多支持创建多少session,可以设的相对小一些,它的默认值是500,这值太大,担心内存溢出。
具体设置还是看你的业务需求
写在结尾:
以上并没有查阅大量文献,仅针对近几个小时观察得出的猜测,如有哪块理解错误请联系我修正。欢迎交流~