转载

解决线上流量过大导致服务超时

问题情况: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左右,之后回落,至此,问题解决

写在后面

问题处理完之后,回想一下,发现有很多不足的地方。

第一个:虽然改动的代码不多也不复杂,但是没有经过测试验证,直接就发到线上,可能带新问题到线上

第二个:prometheus在tomcat处理不过来的时候,直接就没有数据,没办法看到实际的压力是多少,调参数和改代码的时候心里没有底

第三个:增加耗时日志的代码其实没有必要,用arthas这样的诊断工具刚好

第四个:虽然限流器解决了这次的问题,但是并不是最简单有效的办法。tomcat有 server.tomcat.accept-count 这个参数,如果设置的比较小,超出的请求不会被处理,也可以减少tomcat线程的占用。比改代码风险小的多

原文  https://juejin.im/post/5e853aebe51d4546b50d3537
正文到此结束
Loading...