一、问题描述

在进行压力测试过程中,发现客户端访问业务pod访问有延迟,共两个pod,一个延迟大(2-10s),一个延迟小。

二、业务场景分析

client端请求podA的处理过程为

①podA接受请求

②podA请求Pod(geocrowd),成功后执行③

③podA请求Pod(place-search), 成功后执行④

④podA请求Redis实例,成功后返回结果给client端。

三、问题观测和分析过程

阶段一

1.客户做压力测试时问题发生,在podA内手工发送http本地请求,延迟明显,有时延迟2-10s,有时卡住curl不退出。PodB没有明显卡顿。

2. http本地请求卡顿时,podA内手工发送http请求访问Pod(geocrowd),没有明显延迟(没有秒级延迟)

3. http本地请求卡顿时,podA内手工发送http请求访问Pod(place-search),没有明显延迟(没有秒级延迟)

4. 在podA测试redis端口连通性,正常。telnet <redisip> 6379

5. 查看pod的cpu利用率,内存使用率,监控平台查看网卡出入流量,没有发现瓶颈。

6. 与客户沟通,podA设置了liveness和readiness probe,探测http端口,问题发生期间,没有出现异常,说明node本地容器网络没有问题。

pod到redis之间的网络环节不确定是否有问题。

根据6,网络环节①没有问题,根据2和3,网络环节②,③没有问题。

根据4,确定redis IP Port连通性没有问题。但是redis是否有延迟或卡顿不能确认。

基于以上信息,怀疑Redis请求可能会出问题,需要进一步观测。

7.客户对podA没有对各环节做详细的延迟监控,没有关键路径的日志,异常日志等。所以需要继续找其他信息来定位。

8.客户业务(podA, podB)进程开放了golang pprof端口,抓取golang运行时调试信息,方法参见:https://jvns.ca/blog/2017/09/24/profiling-go-with-pprof/

提取goroutine调用栈: curl -o debughttp://10.20.128.151:15033/debug/pprof/goroutine?debug=2

podA与podB的goroutine调用栈对比,发现podA有goroutine 4400+,有4300+跟redis访问相关,都在等待semphore。没有问题的Pod goroutine只有100-400个。

阶段二

1.客户继续压测,继续观测pod状态,

redis实例在可用区prod_sh01, 推测podA和podB的不同表现可能是AZ间网络延迟增加造成的。

2.wireshark抓包分析,发现有redis数据流有异常。

3.转给redis侧分析,结合 go-redis的代码 + tcpdump抓的包 + goroutine调用堆栈

发现有两个问题:

(1) go-redis的cluster pipeline模式依赖command命令,京东云Redis不支持command命令。

在不支持command命令的情况下,所有访问的redis命令都会变成串行执行,并且会多发出一个command命令(2) 发现有时候会发送参数个数为0的mget命令(这个对性能没有影响)

4. redis侧给出解决方案: 升级redis服务,支持command命令

5. 升级完成后,客户重新压测,redis阻塞协程数都为0,tcp连接数达到连接池最大值200.

6.客户反馈业务延迟降低,TP99最高1.5s左右,QPS接近正常。后续逐步切量。

四、结论

中间件Redis不支持command命令,导致客户侧redis client异常,goroutine堆积阻塞,造成卡顿。

五、后续优化项

TP99最高仍有1.5s左右,后续用户切业务中,业务峰值流量是否能支持有待观察,需要继续优化。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐