一、引言
有个接收订单mq进行数据实时分析汇总的服务出现了积压,很明显是处理速度跟不上订单下单速度,要排查的重点就是影响节点处理速度的因素,下面分析一下博主的排查过程。
二、排查
1、qps
首先看了一下节点的qps,订单的qps一直高,并没有太大的区别。
2、mysql
到阿里云看了一下数据库的情况,没有慢sql,不会影响处理速度。
3、jvm
jvm如果经常发生full gc,就会stop the world影响节点处理情况,但是看看监控没什么问题。
4、节点
把节点监控时间拉长,发现了问题。
节点正常消费mq在34ms左右
凌晨处理速度慢到500~600ms
一段时间之后平均处理速度在200~300ms左右 ,但是也远远超出正常处理速度
集群增加两个节点之后,其他三个节点毫无压力,172.17.74.80的处理速度仍然在200~300ms左右
节点重启之后,处理速度恢复正常。
三、分析
只有这个节点处理速度变慢是很奇怪的事情,凌晨出现指明了方向,要看看有什么不同发生在凌晨,最后问到了dba那边,redis在那个时间点升级过,之后节点处理速度就慢如老牛。
服务处理订单mq时使用了redis分布式锁,说明升级引起了这个节点连接redis耗时。
1、redisson加锁
作者公司使用的是redisson链接redis进行分布式加锁:
Redisson使用Redis的SETNX命令(SET if Not eXists)来尝试获取锁。SETNX命令在键不存在时设置键的值,如果键已经存在则不做任何操作。通过SETNX命令,Redisson可以保证只有一个线程能够成功获取到锁。
当一个线程成功获取到锁时,它会设置一个过期时间(expire),以防止锁被长时间占用。这样即使锁的持有者在执行完任务后没有主动释放锁,锁也会在一定时间后自动释放,避免了死锁的情况。
如果一个线程未能成功获取到锁,它可以选择等待或立即返回。Redisson提供了两种等待策略:自旋等待和阻塞等待。自旋等待是指线程不断尝试获取锁,直到成功或超过最大等待时间。阻塞等待是指线程在获取锁失败后,将自己阻塞,直到其他线程释放锁或超过最大等待时间。
当一个线程释放锁时,它会使用Lua脚本来保证释放锁的原子性。Lua脚本将检查锁的持有者是否与当前线程匹配,如果匹配则删除锁。
2、原理分析
redisson加锁底层是通过netty进行redis通信,Redis协议是一种简单的文本协议,基于TCP连接,用于与Redis服务器进行通信。它使用简单的请求-响应模式,客户端发送命令请求给服务器,服务器执行相应的操作并返回响应给客户端。
Redis协议的数据格式是以行为单位进行传输,每个请求或响应都以\r\n(回车换行)作为结束符。请求由命令和参数组成,以多行字符串的形式发送给服务器。响应以不同的数据类型(例如字符串、整数、数组等)进行返回。
Redisson使用Netty作为底层通信框架,通过Netty的Channel进行数据的读写和传输。当Redisson需要与Redis服务器进行通信时,它会构造符合Redis协议的请求,并通过Netty的Channel将请求发送给Redis服务器。同样,当Redis服务器返回响应时,Redisson会通过Netty的Channel接收响应数据,并解析成相应的数据类型。
作者理解的方向是当Redis服务器返回响应数据时,Netty负责接收数据并传递给Redisson, Redisson使用自己的解析器来处理Redis协议的数据。
redis的升级的时候,那个节点正在进行分布式锁加锁,Redisson的解析器受到影响。因为Redisson的解析器在处理Redis数据时,会根据不同的Redis版本做一些适配和兼容性处理。这是因为不同的Redis版本可能会引入新的数据类型、命令或协议规范。
但是作者在后面没法复现,毕竟redis的升级再加上节点正好链接失败通信出现问题,这个太巧合了。
四、总结
如果有redis或者mysql、mongo之类升级过后,节点平时正常功能产生了问题,可以将节点重启看是否能解决。
排查问题的过程其实是推测加上试错的过程,只要不影响业务就可以尝试去操作。