问题情况:track是APP的埋点服务。前段时间由于疫情,APP用户量暴增,服务器资源紧张,暂时关闭了服务。因为只是累计数据,并没有太多的统计需求,大家都忘了把服务重新跑起来... 直到有个新的数据统计需求。由于移动端SDK的数据推送带有重试,不要担心数据丢失的问题。然而,把服务重新启动后,直接503。现在开始处理问题 排查过程:应用的最外层是nginx,因为不确定是服务的问题还是nginx配置,直接登录到服务器用内网请求服务,发现超时严重,一次请求要30多秒才能返回,显然问题就在服务上。
登录grafana,看到的QPS趋势图大概到几百之后,直接就没数据了。
prometheus获取数据的方式是定时请求 /actuator/prometheus
。服务大量超时,prometheus也没办法获取到数据,可能是因为tomcat已经扛不住了。
解决过程:
第一个想到的是基于之前的经验想增大tomcat的线程数解决。直接在一个节点上修改启动参数上 -Dserver.tomcat.max-threads=1000
,尝试验证想法,启动后流量进来后还是卡死,再来一次,直接改到8k,之后稍微好点,grafana上能看到qps稍微上来一点,但是几分钟后,grafana上的曲线又断了,这时,qps只有几百,而请求耗时超过10秒。还是没抗住...
发现此路不通,开始尝试从代码里找性能问题。埋点服务只有一个接口被移动端调用,而这个接口做的事情只是收到请求后把数据简单解析出来扔到kafka里,代码很简单,除了发送信息到kafka,其他地方都不大有可能耗时很久。尝试改动代码,记录发送信息到kafka的耗时,代码大概是
long startTime = System.currentTimeMillis(); //发送消息到kafka log.info("耗时:{}",(System.currentTimeMillis()-startTime)); 复制代码
重启服务,刚开始日志打印的耗时只有毫秒级,之后慢慢变成秒,之后变成几十秒。这个地方有问题! 检查kafka producer的batchSize,只有50,确实有点小。简单粗暴增加producer的batchSize到1k,服务重启后多坚持了几分钟,不过还是挂。
两次不顺,尝试先让自己冷静下来。服务关闭到现在大概一个月,如果数据上报不成功,之后会一边累积新的数据,一边重试上报,这个数据量确实会比较大。但是峰值只有那么几天,之后访问量就不那么大了,也就是说现在遇到的问题只是临时的,这批数据处理完,流量就会趋于平稳。
流量洪峰导致服务被压到503,解决办法之一是限流。大概预估出服务的承载能力,限制住流量,超出处理能力的请求,直接返回500,埋点SDK还会重试,数据也不会丢。正好直到guava有一个叫RateLimiter的工具,直接撸出代码如下
public static final RateLimiter RATE_LIMITER = RateLimiter.create(1000); @RequestMapping public ResponseDataWrapper<Object> trackEvent(SADataWrapper saDataWrapper) { if (RATE_LIMITER.tryAcquire()) { throw new RateLimitException(); } //省略业务处理代码 } 复制代码
和预期一样,上线后,刚开始流量稳定在1k左右,之后回落,至此,问题解决
server.tomcat.accept-count
这个参数,如果设置的比较小,超出的请求不会被处理,也可以减少tomcat线程的占用。比改代码风险小的多