现象倒推一:Java Web应用的连接数暴增
最大的可能是,Web应用的线程调用路径中阻塞在某个远端资源上。
- 线程向某个远端资源发起的请求被阻塞,可能是以下原因:
- 连接受阻,如等待client端连接池的空闲连接,如远端服务连接数满;
- 响应迟迟没有返回,如数据库中的记录被“表锁”或“行锁”,如数据库有大量慢查询;
常见的连接超时时间
为了让
大家一看到线上日志某些刚刚好的时间就能反应过来,总结如下:
- memcache
- PHP下,Memcache::connect 函数传入的 timeout 参数代表连接超时时间,单位秒。默认值1秒;
- 注:修改此值之前请三思,过长的连接超时时间可能会导致失去所有的缓存优势。
- Java下,
- spymemcached 里,配置 opTimeout 代表操作超时时间,默认值2.5秒;
- xmemcahced 里,opTimeout 的定义与spy 一样,默认值1秒;
- mysql
- wait_timeout:服务器关闭非交互连接之前等待活动的秒数,默认值28800秒(即8小时);
- connect_timeout:在获取链接时,等待握手的超时时间,只在登录时有效,默认值10秒;
- innodb_lock_wait_timeout:一个 InnoDB 事务遇到一个行锁,等待的超时时间,默认值50秒,届时会打印“Lock wait timeout exceeded; try restarting transaction”错误;
- mongodb
- Java下,
- MongoOptions.maxWaitTime:连接上阻塞线程的最大等待时间,默认值120秒。
- MongoOptions.connectTimeout:建立新连接超时时间, (注意Only used for new connections) 默认无限制。
- MongoOptions.socketTimeout:socket通讯超时时间,默认无限制。
现象倒推二:Java应用频繁 fullgc
频繁 fullgc 大致有几种原因:
第一种,还是由于某一个资源成为瓶颈,导致大量线程进入 blocked 状态,新的 Requests 源源不断进入,不断开启新线程。加之线程在内存中做了很多运算,且这些内存无法收回,导致 old generation(旧生代内存区)占用比例超过阈值(此阈值由 JVM 参数 CMSInitiatingOccupancyFraction设定,默认值是90%),进一步导致新对象分配没有更多的空间,从而频繁触发 fullgc。
举例,由于某段代码没有释放数据库连接——>连接池中的连接耗尽——>部分线程无限 TIMED_WAITING ——>其余线程都 Blocked——>开启新线程——>频繁引发GC——>占用大量CPU——>应用挂起。
第二种,产生了大量大内存对象,占用了大量堆空间,引发 fullgc 。
可以将当时的 memory dump 文件经由 MAT 工具分析,找到对应的对象或直接找到类。