一、通过监控、日志报警发现程序出现问题
1、服务的所有接口都慢或异常,通常是因为公共原因引起的。比如gc、数据库、redis、网络等。首先查看gc监控,查看gc耗时,线程数是否出现暴涨。数据库问题通过sql监控和连接池监控,查看sql执行是否慢,是否出现连接获取超时的情况。redis同样查看监控观察耗时是否有抖动。必要时配合业务日志的requestId查看。
2、部分机器出现接口慢或者异常。首先按照步骤1排查问题,找出拖慢的资源,确认机器所在的机房。如果有问题的服务都在同一个机房通常是由于访问了跨机房的资源,和运维确认是否出现了专线抖动。
3、服务的部分接口出现超时或者异常。首先查看这些接口的调用量是否有暴涨,然后选择超时的接口按照1步骤排除问题,还要关注第三方调用耗时情况。第三方调用耗时有几种情况:1、被调用服务处理耗时确实变长。2、网络耗时,比如走了外网、偶尔dns解析慢等。如果被调是内部服务可以在nginx层查看被调服务的响应时间,如果nginx层的响应时间都很小,那么基本上确定是由于网络原因,可以找运维一起排查是否是网络原因导致的;如果是外部接口,首先联系相关业务对接人查看是否是对方服务导致的,然后再排查网络问题。
4、如果在某一时刻某一个接口或者某几个接口开始出现耗时长的问题,接着这个服务的所有接口都出现这个问题。通常是由于最开始慢的几个接口,把整个服务拖挂了。比如访问mongo数据库慢,并且客户端没有设置超时时间,如果这个接口的调用量持续增加就会把tomcat的线程数都占满,最终导致服务的所有请求没有线程来处理。
总结:排查问题的思路首先从外部依赖入手,比如第三方调用、db、redis等,如果查了都没有问题,那说明是服务内部出了问题,这个可以借助业务日志查问题,有时候还要用arthas具体分析本地某个方法的调用耗时。如果服务的调用量暴涨,要在elk上查看调用来源ip确定是哪个服务导致的。
二、开发中需要注意的几个点
1、第三方调用一定要设置合理的超时时间。核心、流量大的接口第三方调用超时时间应该设置在200ms以内,避免一次请求多次调用相同的第三方接口(可以考虑批量,批量要加个数限制);非核心、流量小的接口可以根据具体服务响应时间设置一般不超过3s。对于第三方调用,如果核心链路尽量设置好开关,做好降级方案。
2、公共组件的访问,比如redis、mysql、mongo等等一定也要设置合理的超时时间,不要因为部分操作慢导致整个服务被拖垮。mysql一般都会用到连接池,从连接池获取的超时时间一般设置成1-3s之间,建立连接时间1-3s,一般获取连接时间设置要稍微大于连接的建立时间,mysql驱动一定要设置读取超时时间socketTime=1-5s之间(在jdbcUrl中设置)。mongo客户端超时时间设置,serverSelectionTimeout:找到可用的MongoDB Server的时间,一般设置3-10s之间;connectTimeout:和MongoDB Server建立Connection的时间1-3s;maxWaitTime:从Connection Pool中获取Connection的时间,一般设置2-5s;socketTimeout:获取到连接后,进行socket通信,数据传输时间,一般设置1-3s。
3、合理的设置tomcat的线程数,避免多个docker共享一个宿主机器导致宿主机器负载高,线程调度不过来。线程池大小的设置需要根据自己的业务设置,比如预估服务正常的流量下线程数大概在500左右,预留一定buffer设置成600-700左右。
4、包的引用要清晰。不要存在包冲突问题。比如rocketmq有两个版本,一个是ali的,一个是apache。如果同时依赖了两个包,并且程序里有的引用的是ali的包,有的引用的是apache的包,这样在启动的时候就会遇到问题。
5、命名要规范,java采用驼峰,写代码要遵循阿里编程手册。
超时时间的设置,只是建议还是要根据自己业务的特殊情况确定,比如后台服务一次查询时间很长,那么socketTime时间就不能设置的过低。
三、出现过的问题举例
1、预估服务占用cpu过高。通过arthas工具查看是因为通过反射copy大对象的属性,改成set方式。命令thread -n 20具体的命令参见官网。
2、预估查询渠道优惠配置,由于数据库抖动导致预估接口变慢,并且拖累db。原因是:db出现抖动(sql各种join写法),导致连接池被占满(连接数30),然后多余的请求从连接池获取连接的时间变慢有的甚至超时,由于线程没有及时释放,新的请求进来导致tomcat线程数翻倍由原来的500上升到1000,导致cpu负载变高,调度不过来,好多sql结果已经返回了但是客户端由于机器负载高线程没有被及时调度,所以导致从mysql server端看有好多并行sql,这样就导致了恶性循环。解决办法,查询渠道优惠增加缓存,tomcat最大线程数设置600,迁移预估服务避免多个预估实例在同一台宿主机器上。
四、核心链路基础监控组件
1、sql执行时间,成功次数和失败次数等监控。如果用到连接池要有从连接池获取超时的监控。
2、mongo执行sql时间,成功次数和失败次数等监控。
3、redis操作耗时、成功和失败次数等监控。
4、rocketmq生产者发送成功和失败次数监控,消费者消费delay的监控。
5、如果用到binglog,要有binlog的消费延时监控。
6、接口要有调用成功次数,失败次数和耗时的监控。这样可以了解到核心接口的平时调用量,可以适当的设置合理的限流策略,避免意外流量大幅度增加导致服务被打挂。还有就是核心接口和其他非核心的接口尽量分开部署,避免相互影响,最基本的得把定时任务、消费者这种服务和api接口分开。
7、第三方调用要加调用耗时、成功次数和失败次数监控,合理的设置调用超时时间。
8、业务代码中如果使用了线程池,要对线程池内的线程数和任务队列长队进行监控。