1. 先说情况:
浏览器访问项目一直转圈,出不来页面,后台接口也访问不了
2. 定位过程:
使用ps aux | grep java查询进程
Tomcat进程存在,查看日志没有报错,但是日志不再打印
根据进程pid查看服务的线程:jstack 19746
发现有大量WAITING的线程,定位到代码是从apache对象池获取对象的方法
进入到源码就发现有个获取最大等待时间的方法,进去一看默认值是-1
再检查了一下池子设置的大小,只有5
那就很明显了,对象池大小不够,导致超过5人请求时就会等待,而且没有等待超时时间,等待的线程多了就把Tomcat的线程池给撑爆了,因为是小型项目,看了一下WAITING的正好是200个,和Tomcat默认的线程池大小一致。
3. 解决方案:
因为项目同时使用的人数在20+人左右,设置对象池的大小为50,超时时间为5000毫秒
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// 50
poolConfig.setMaxIdle(threadPoolSize);
poolConfig.setMaxTotal(threadPoolSize);
poolConfig.setMinIdle(threadPoolSize);
// 5000
poolConfig.setMaxWaitMillis(maxWaitMillis);
顺便列一下对象池配置各个参数的含义:
public static class Config {
// 允许最大空闲对象数
public int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
// 允许最小空闲对象数
public int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;
// 允许最大活动对象数
public int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
// 允许最大等待时间毫秒数
public long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
// 当池中对象用完时,请求新的对象所要执行的动作
public byte whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
// 是否在从池中取出对象前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
public boolean testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
// 是否在向池中归还对象前进行检验,如果检验失败
public boolean testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
// 连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除
public boolean testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
// 在空闲连接回收器线程运行期间休眠的时间毫秒数. 如果设置为非正数,则不运行空闲连接回收器线程
public long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
// 设定在进行后台对象清理时,每次检查对象数
public int numTestsPerEvictionRun = GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
// 被空闲对象回收器回收前在池中保持空闲状态的最小时间毫秒数
public long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
// 被空闲对象回收器回收前在池中保持空闲状态的最小时间毫秒数
public long softMinEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
// 是否采用后进先出策略
public boolean lifo = GenericObjectPool.DEFAULT_LIFO;
}