太坑了吧!一次某某云上的redis读超时排查经历

这篇具有很好参考价值的文章主要介绍了太坑了吧!一次某某云上的redis读超时排查经历。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一次排查某某云上的redis读超时经历

性能排查,服务监控方面的知识往往涉及量广且比较零散,如何较为系统化的分析和解决问题,建立其对性能排查,性能优化的思路,我将在这个系列里给出我的答案。

问题背景

最近一两天线上老是偶现的redis读超时报警,并且是业务低峰期间,甚是不解,于是开始着手排查。

以下是我的排查思路。

排查思路

查阅 redis 慢查询日志

既然是redis超时,首先想到的还是 对于redis的操作命令存在慢查询导致的。
太坑了吧!一次某某云上的redis读超时排查经历
redis的慢查询阈值是10ms,唯一的慢查询是备份时的bgrewriteaof语句,并不是业务命令,既然从慢查询很日志看不出端倪,那就从redis服务器本身查找问题,所以我又去看了redis服务机器的各项硬件指标。

检查 报警期间 redis 各项负载指标

看了下各项监控指标,cpu,内存,qps等等,毫无意外的正常。(这里就不放图了,应为太正常了,分析意义不大)

既然服务端看不出毛病,那是不是客户端的问题,于是我又去检查了我们ecs服务器这边机器的情况。

检查报警期间 ecs服务器的各项指标数据

cpu,内存,带宽等等也是正常且处于较低水平。

排查到这里,重新思考慢查询日志究竟是什么?

慢查询记录的真的是redis命令执行的所有时间吗?redis命令完整的执行过程究竟是怎样的?
慢查询日志仅仅是记录了命令的执行时间,而整个redis命令的生命周期是这样。

客户端命令发送->redis服务器接收到命令,放入队列排队执行->命令执行->返回给客户端结果

那么有没有办法监控到redis的延迟呢?如何才能知道redis的命令慢不是因为执行慢,而是这个过程当中的其他流程慢导致的呢?

redis 提供了监控延迟的工具
开启延迟,设置延迟阀值

CONFIG SET latency-monitor-threshold 100

查阅延迟原因

latency latest

但是这个工具真正实践起来的效果并不能让我满意,因为似乎他并不能把网络因素考虑其中,实践起来的效果它应该是只能将命令执行过程中可能导致延迟的原因分析出来,但是执行前以及执行后的命令生命周期的阶段并没有考虑进去。

当我开启这个工具监控线上redis情况,当又有读超时出现时,latency latest 并没有返回任何延迟异常。

再思考究竟读超时是个什么问题?

客户端发出去了命令,然后阻塞等待redis服务端读的结果,如果没有结果返回,就会触发读超时发生。在go里面代码是如何实现的。

我们用的redis客户端是go-redis

func (c *baseClient) pipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
	err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
		return writeCmd(wr, cmds...)
	})
	if err != nil {
		setCmdsErr(cmds, err)
		return true, err
	}
    // 看到将读取操作用WithReader装饰了一下
	err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
		return pipelineReadCmds(rd, cmds)
	})
	return true, err
}

读取redis服务端响应的方法 是用cn.WithReader进行了装饰。

// 读取之前设置了超时时间
func (cn *Conn) WithReader(timeout time.Duration, fn func(rd *proto.Reader) error) error {
	_ = cn.setReadTimeout(timeout)
	return fn(cn.rd)
}

而cn.WithReader 里,首先便是设置此次读取的超时时间。如果在规定的超时时间内,需要读取的结果没有全部返回也会导致读超时的发生,那么会不会是由于返回结果过多导致读取耗时验证呢?

具体的分析了下报警出错的命令,有些命令比如set命令不需要返回结果都有超时的情况,所以排除掉了返回结果过大的问题。

再次深入思考golang 里的读超时触发过程

go协程在碰到网络读取时,协程会挂起,等待网络包到达后,由go runtime唤醒协程,然后协程继续进行读取操作,当唤醒时会检查超时时间,如果到达了超时限制,那么将直接报读超时错误。(有机会可以详细分析下golang的netpoll源码) 源码如下,

src/runtime/netpoll.go:303
for !netpollblock(pd, int32(mode), false) {
		errcode = netpollcheckerr(pd, int32(mode))
		if errcode != pollNoError {
			return errcode
		}
		// Can happen if timeout has fired and unblocked us,
		// but before we had a chance to run, timeout has been reset.
		// Pretend it has not happened and retry.
	}

netpollblock 不阻塞协程时,首先执行了netpollcheckerr,netpollcheckerr检查是否有超时情况发生。

从唤醒到协程被调度执行的这个时间称为协程的调度延迟,如果这个延迟过高,那么是有可能发生读超时的。
于是我又看了go进程中协程的调度延迟,在golang里 内置了一个/sched/latencies:seconds 指标,代表协程调度延迟,目前的prometheus client 已经对这个指标进行了兼容,所以我们是直接利用它 将延迟耗时在grafana里进行了展示。

超时期间,grafana里的 协程调度延迟只有几毫秒。而超时时间设置的200ms,显然也不是协程调度延迟的问题。

用上终极武器-抓包

以上的思路都行不通了,所以只能用上终极武器,抓包。 看看触发200ms超时时 究竟是哪些包没有到达。因为只能在客户端测抓包,所以接下来的抓包文件都是客户端测的抓包。

很对时候抓包都是解决问题特别是网络问题的最终手段,你能通过抓包清楚的看到客户端,服务端在做什么事情。

为了百分百确认并且定位问题,我一共抓取了3个文件,首先来看下第一个文件。
太坑了吧!一次某某云上的redis读超时排查经历

6379端口号是目的端口,也就是redis的端口,36846是我客户端的端口。

从抓包文件中,发现760054号报文发生了超时重传,如果客户端发了包,但是服务端没有回应ack消息就会触发超时重传,重传之后,客户端也没有收到服务端的消息,并且可以看到发送挥手信号和前一个正常发送的包之间刚好是隔了差不多200ms,而200ms正是客户端设置的超时时间,应用层触发超时后,将调用close方法关闭链接,所以在760055号包里 客户端发送了一个fin 挥手信号代表客户端要关闭链接了。 客户端发送fin信号挥手之后呢,服务端才发来携带数据的ack消息,不过由于此时客户端已经处于要关闭状态,所以直接发送rst信号了。

整个发包和接包流程可以用下面的流程图来展示。

太坑了吧!一次某某云上的redis读超时排查经历

接着来看第二个抓包文件。
太坑了吧!一次某某云上的redis读超时排查经历

抓包中出现大量TCP Dup Ack 的消息,客户端一直在向端口为6379的服务端发送ack的序号为 13364573,代表客户端已经接收到服务端序号13364573之前的包了,然而服务端连续发送的包序号seq都大于了13364573 ,所以客户端认为服务端序号seq是13364573的包丢了,所以随着服务端每一次发送消息过来,都告诉服务端,我应该接收序号是13364573开始的包,赶紧发送过来。

最终在1777232号包,客户端又一次发送了TCP Dup Ack 消息,催促服务端赶紧把丢掉的包发过来,不过这次服务端连回应都不回应了,最终触发了客户端应用层200ms的超时时间,调用close方法关闭了连接。所以在1778166号包里,可以看到客户端发送fin挥手信号,1778166 号包的发送时间和1777232号包的发送时间正式相隔了200ms。

整个发包和接包流程可以用下面的流程图来展示。
太坑了吧!一次某某云上的redis读超时排查经历

再来看第三个抓包文件,第三个抓包文件是我将客户端超时时间设置为500ms后出现超时情况时抓到的。
太坑了吧!一次某某云上的redis读超时排查经历

首先看第一个红色箭头处,也就是911752号包,它被wireshark标记为 Tcp Previous segment not captured,表示这个包之前的包没有被捕获到,说明这个包seq序号之前的包存在丢包情况。发现它前一个包也就是911751号包也是服务端发来的包,并且next seq 是18428124,说明911751号包下一个包的seq应该是18428124,但是911752的seq是18429584,进一步说明 来自服务端的包序号在18428124到18429584之间的包存在丢包情况

接着是客户端对911751号包的ack消息,说明序号是18428124之前的包已经全部接收到。然后接受到了911754号这个来自服务端的包,并且这包的开始seq序号是18433964,而最近一次来自服务端的911752号包的next seq是18432504,说明在18432504 和18433964之间,也存在服务端的丢包情况,所以911754号包也被标记为了Tcp Previous segment not captured。

接下来就是服务端重传包,客户端继续回应Ack的过程,但是这个过程直到914025号时就停止了,因为整个读取服务端响应的过程从开始读 到当前时间已经达到了应用层设置的500ms,所以在应用层直接就关闭掉了这个链接了。

看到现在,我有了充足的理由相信,是云服务提供商那边的问题,中间由于网络丢包的原因,且延迟较大导致了redis的读超时。拿着这些证据也说服了他们,并最终圆满解决。

提工单,云服务商的排查支持

太坑了吧!一次某某云上的redis读超时排查经历

圆满解决文章来源地址https://www.toymoban.com/news/detail-411371.html

到了这里,关于太坑了吧!一次某某云上的redis读超时排查经历的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 劝退忠告:外包实在是太坑了,划水三年,感觉人都废了

    先说一下自己的情况,专科生,19年通过校招进入杭州某个外包软件公司,干了接近3年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!   而我已经在一个企业干了3年的功能测试,已经让我变得不思进取,谈了几年的女朋友

    2024年02月08日
    浏览(30)
  • docker在阿里云上的镜像仓库管理

    目录 一.阿里云 1.登录进入阿里云网站,点击个人实例进行创建 2.创建仓库,填写相关信息 3.在访问凭证中设置固定密码用于登录,登录时用户名是使用你注册阿里云的账号名称,密码使用设置的固定密码 4.为镜像打标签并推送到仓库 5.拉取镜像验证 二.distribution 1.扩展源下载

    2024年02月11日
    浏览(27)
  • 论文解读 | KPConv——点云上的可形变卷积网络

    原创 | 文 BFT机器人  《KPConv: Flexible and Deformable Convolution for Point Clouds》是一篇发表于2019年的研究论文,作者为Hugues Thomas、Charles R. Qi、Jean-Emmanuel Deschaud、Beatriz Marcotegui和François Goulette。这篇论文关注于点云数据上的卷积操作,提出了一种名为KPConv的卷积方法,旨在解决点云

    2024年02月09日
    浏览(26)
  • JAVA调用Bartender进行标签打印(可本地用打印机客户端进行测试打印,【云上的项目】可通过WebSocket进行通讯进行打印)

    用Java编写一个打印标签客户端  点击运行启动会打开首页  可以点击预览打印  点击打印可测试成功     打印机结果  前端用的是thymeleaf 代码片段 后端代码  页面上预览图片和真实打印机打出来的不一致是正常的因为这是测试打印(测试本地电脑是否符合使用打印机客户

    2024年02月15日
    浏览(39)
  • 记一次 .Net+SqlSugar 查询超时的问题排查过程

    环境和版本:.Net 6 + SqlSuger 5.1.4.*   ,数据库是mysql 5.7 ,数据量在2000多条左右 业务是一个非常简单的查询,代码如下: tb_name 下配置了一对多的关系导航,但是执行时没有include导航属性,当执行上述代码时,查询非常慢,甚至会超时报错: The Command Timeout expired before the o

    2024年02月07日
    浏览(34)
  • 一次生产环境上的dockerd启动失败原因分析

    今夜原计划对 生产环境 上的 SDN 组件进行一次紧急扩容操作的,但业务基础环境中的 Docker-Engine 启动不起来了、原定计划也就无法继续进行了。 尽管查清了基础业务环境中的故障原因,但金主DD说今天先不干了,那就整理整理思路写篇流水账吧 。。。 现象如下: 1 ps -aux 查

    2024年03月10日
    浏览(51)
  • Spring Boot整合Redis实现订单超时处理

    🎉欢迎来到架构设计专栏~Spring Boot整合Redis实现订单超时处理 ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹 ✨博客主页:IT·陈寒的博客 🎈该系列文章专栏:架构设计 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习 🍹文章作者技术和水平有限,如果文

    2024年02月03日
    浏览(31)
  • 记录开发环境docker上的一次springboot无法读取更新的配置文件的问题

    背景:一般开发环境的管理不是很严格,当对代码进行一些组件的添加时,往往需要修改spring的配置文件,有的时候为了保险起见,回预先备份原本的配置文件,我采取在./config中创建了一个名为bak-日期的目录,将原本的配置文件mv到该目录下,将新的配置文件移到config目录

    2024年02月11日
    浏览(38)
  • 异常:Springboot中redis使用lettuce连接池经常连接超时解决

    环境 依赖 配置 1. 问题点:项目启动后,一段时间过后redis自动掉线 2. 问题分析: 这是lettuce-core的实现里,有类似心跳机制的保持长连接方式,不过心跳机制是不停的来回发心跳包直到连接不可用再去被动重新连接,而lettuce的方案是将连接池里处于空闲(idle)状态的client每

    2024年02月11日
    浏览(43)
  • 记一次 springboot集成kafka-本地连接服务器的kafka,连接不上的问题

    yml中配置了bootstrap-servers: 服务器地址:9092 ,但是连接时却报了 Connection to node -1 ( localhost/127.0.0.1:9092 ) could not be established. chat给我的回复如下,通过一些列检查我确定了在服务器上,kafka没有问题 最后还是从一篇博客中的第一句话得到了答案,博客链接放在最下方 我是docker安

    2024年01月17日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包