孤独酒馆|6000+字,30+张图。JAVA线上故障排查全套路总结( 五 )


孤独酒馆|6000+字,30+张图。JAVA线上故障排查全套路总结触发fullGCG1中更多的还是mixedGC , 但mixedGC可以和youngGC思路一样去排查 。 触发fullGC了一般都会有问题 , G1会退化使用Serial收集器来完成垃圾的清理工作 , 暂停时长达到秒级别 , 可以说是半跪了 。 fullGC的原因可能包括以下这些 , 以及参数调整方面的一些思路:

  • 并发阶段失败:在并发标记阶段 , MixGC之前老年代就被填满了 , 那么这时候G1就会放弃标记周期 。 这种情况 , 可能就需要增加堆大小 , 或者调整并发标记线程数-XX:ConcGCThreads 。
  • 晋升失败:在GC的时候没有足够的内存供存活/晋升对象使用 , 所以触发了Full GC 。 这时候可以通过-XX:G1ReservePercent来增加预留内存百分比 , 减少-XX:InitiatingHeapOccupancyPercent来提前启动标记 , -XX:ConcGCThreads来增加标记线程数也是可以的 。
  • 大对象分配失败:大对象找不到合适的region空间进行分配 , 就会进行fullGC , 这种情况下可以增大内存或者增大-XX:G1HeapRegionSize 。
  • 程序主动执行System.gc():不要随便写就对了 。
另外 , 我们可以在启动参数中配置-XX:HeapDumpPath=/xxx/dump.hprof来dump fullGC相关的文件 , 并通过jinfo来进行gc前后的dump
jinfo -flag +HeapDumpBeforeFullGC pid jinfo -flag +HeapDumpAfterFullGC pid这样得到2份dump文件 , 对比后主要关注被gc掉的问题对象来定位问题 。
网络涉及到网络层面的问题一般都比较复杂 , 场景多 , 定位难 , 成为了大多数开发的噩梦 , 应该是最复杂的了 。 这里会举一些例子 , 并从tcp层、应用层以及工具的使用等方面进行阐述 。
超时超时错误大部分处在应用层面 , 所以这块着重理解概念 。 超时大体可以分为连接超时和读写超时 , 某些使用连接池的客户端框架还会存在获取连接超时和空闲连接清理超时 。
  • 读写超时 。 readTimeout/writeTimeout , 有些框架叫做so_timeout或者socketTimeout , 均指的是数据读写超时 。 注意这边的超时大部分是指逻辑上的超时 。 soa的超时指的也是读超时 。 读写超时一般都只针对客户端设置 。
  • 连接超时 。 connectionTimeout , 客户端通常指与服务端建立连接的最大时间 。 服务端这边connectionTimeout就有些五花八门了 , jetty中表示空闲连接清理时间 , tomcat则表示连接维持的最大时间 。
  • 其他 。 包括连接获取超时connectionAcquireTimeout和空闲连接清理超时idleConnectionTimeout 。 多用于使用连接池或队列的客户端或服务端框架 。
我们在设置各种超时时间中 , 需要确认的是尽量保持客户端的超时小于服务端的超时 , 以保证连接正常结束 。
在实际开发中 , 我们关心最多的应该是接口的读写超时了 。
如何设置合理的接口超时是一个问题 。 如果接口超时设置的过长 , 那么有可能会过多地占用服务端的tcp连接 。 而如果接口设置的过短 , 那么接口超时就会非常频繁 。
服务端接口明明rt降低 , 但客户端仍然一直超时又是另一个问题 。 这个问题其实很简单 , 客户端到服务端的链路包括网络传输、排队以及服务处理等 , 每一个环节都可能是耗时的原因 。
TCP队列溢出tcp队列溢出是个相对底层的错误 , 它可能会造成超时、rst等更表层的错误 。 因此错误也更隐蔽 , 所以我们单独说一说 。
孤独酒馆|6000+字,30+张图。JAVA线上故障排查全套路总结如上图所示 , 这里有两个队列:syns queue(半连接队列)、accept queue(全连接队列) 。 三次握手 , 在server收到client的syn后 , 把消息放到syns queue , 回复syn+ack给client , server收到client的ack , 如果这时accept queue没满 , 那就从syns queue拿出暂存的信息放入accept queue中 , 否则按tcp_abort_on_overflow指示的执行 。


推荐阅读