首页 > 其他分享 >[转帖]解Bug之路-串包Bug

[转帖]解Bug之路-串包Bug

时间:2024-02-28 09:11:54浏览次数:22  
标签:get 系统 Redis 转帖 串包 Jedis Bug

https://zhuanlan.zhihu.com/p/225394262

 

解Bug之路-串包Bug

笔者很热衷于解决Bug,同时比较擅长(网络/协议)部分,所以经常被唤去解决一些网络IO方面的Bug。现在就挑一个案例出来,写出分析思路,以飨读者,希望读者在以后的工作中能够少踩点坑。

串包Bug现场

前置故障Redis超时

由于某个系统大量的hget、hset操作将Redis拖垮,通过监控发现Redis的CPU和IO有大量的尖刺,CPU示意图下图所示:


CPU达到了100%,导致很多Redis请求处理不及时,其它业务系统都频繁爆出readTimeOut。此时,紧急将这个做大量hget、hset的系统kill,过了一段时间,Redis的CPU恢复平稳。

一波未平,一波又起

就在我们以为事件平息的时候,线上爆出登录后的用户名称不正确。同时错误日志里面也有大量的Redis返回不正确的报错。尤为奇葩的是,系统获取一个已经存在的key,例如get User123456Name,返回的竟然是redis的成功返回OK。示意图如下:

Jedis.sendCommand:get User123456Name
Jedis.return:OK
    or
Jedis.sendCommand:get User123456Name
Jedis.return:user789

我们发现此情况时,联系op将Redis集群的所有Key紧急delete,当时监控示意图:


当重启后,我们再去线上观察的时候,发现错误依然存在,神奇的是,这种错误发生的频率会随着时间的增加而递减。到最后刷个10分钟页面才会出现这种错,示意图如下所示:


既然如此,那只能祭出重启大法,把出错的业务系统全部重启了一遍。
重启之后,线上恢复正常,一切Okay。

Bug复盘

此次Bug是由Redis本身Server负载太高超时引起的。Bug的现象是通过Jedis去取对应的Key值,得不到预期的结果,简而言之包乱了,串包了。

缩小Bug范围

首先:Redis是全球久经考验的系统,这样的串包不应该是Redis的问题。
第二:Redis刷新了key后Bug依然存在,而业务系统重启了之后Okay。
第三:笔者在错误日志中发现一个现象,A系统只可能打印出属于A系统的json串结构(redis存的是json)。
很明显,是业务系统的问题,如果是Redis本身的问题,那么在很大概率上A系统会接收到B系统的json串结构。

业务系统问题定位

业务系统用的是Jedis,这同样也是一个久经考验的库,出现此问题的可能性不大。那么问题肯定是出在运用Jedis的姿势上。
于是笔者找到了下面一段代码:

public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
    JedisClient jedisClient = jedisPool.getResource();   
    try{
      return method.invoke(jedisClient,args);  
    } catch(Exception e){
      logger.error("invoke redis error",e);   
      throw e;   
    }finally {
        if(jedisClient != null){
            // 问题处在下面这句
            jedisPool.returnResource(jedisClient);
        }
    }
}

当时我就觉得很奇怪,笔者自己写的,阅读过的连接池的代码都没有将抛异常的连接放回池里。就以Druid为例,如果是网络IO等fatal级别的异常,直接抛弃连接。这里把jedisClient连接返回去感觉就是出问题的关键。

Bug推理

笔者意识到,之所以串包可能是由于jedisClient里面可能有残余的数据,导致读取的时候读取到此数据,从而造成串包的现象。

串包原因

正常情况下的redis交互

先上Jedis源码

public String get(final String key) {
    checkIsInMulti();
    client.sendCommand(Protocol.Command.GET, key);
    return client.getBulkReply();
}

Jedis本身用的是Bio,上述源码的过程示意图如下:

 

出错的业务系统的redis交互

 


由于Redis本身在高负载状态,导致没能及时相应command请求,从而导致readTimeOut异常。

复用这个出错链接导致出错

在Redis响应了上一个command后,把数据传到了对应command的socket,进而被inputream给buffer起来。而这个command由于超时失败了。


这样,inputStream里面就有个上个命令留下来的数据。
下一次业务操作在此拿到这个连接的时候,就会出现下面的情况。


再下面的命令get user789Key会拿到get user456Key的结果,依次类推,则出现串包的现象。

串包过程图

 


上图中相同颜色的矩形对应的数据是一致的。但是由于Redis超时,导致数据串了。

为什么get操作返回OK

上图很明显的解释了为什么一个get操作会返回OK的现象。因为其上一个操作是set操作,它返回的OK被get操作读取到,于是就有了这种现象。

为什么会随着时间的收敛而频率降低

因为在调用Redis出错后,业务系统有一层拦截器会拦截到业务层的出错,同时给这个JedisClient的错误个数+1,当错误个数>3的时候,会将其从池中踢掉。这样这种串了的连接会越来越少,导致Bug原来越难以出现。

在每次调用之前清理下inputstream可行否

不行,因为Redis可能在你清理inputstream后,你下次读取前把数据给传回来。

怎么避免这种现象?

抛出这种IO异常的连接直接给扔掉,不要放到池子里面。

怎么从协议层面避免这种现象

对每次发送的命令都加一个随机的packetId,然后结果返回回来的时候将这个packetId带回来。在客户端每次接收到数据的时候,获取包中的packetId和之前发出的packetId相比较,如下代码所示:

if(oldPacketId != packetIdFromData){
     throw new RuntimeException("串包");
}

总结

至少在笔者遇到的场景中,出现IO异常的连接都必须被抛掉废弃,因为你永远不知道在你复用的那一刻,socket或者inputstream的buffer中到底有没有上一次命令遗留的数据。
当然如果刻意的去构造协议,能够通过packetId之类的手段把收发状态重新调整为一致也是可以的,这无疑增加了很高的复杂度。所以废弃连接重建是最简单有效的方法。

标签:get,系统,Redis,转帖,串包,Jedis,Bug
From: https://www.cnblogs.com/jinanxiaolaohu/p/17959901

相关文章

  • 一次把事情做对:对质量保证要做的刚刚好,不浪费资源不遗漏BUG
    知道你有没有注意到,走进各个企业,总能看到那么几句振奋人心的标语,其中“一次把事情做对”绝对是个高频词汇。以前每次看到,我都会想:这家企业也太教条了,都什么时代了,对失败这么零容忍,还怎么创新呢?这个时代的主旋律不是从错误中学习,快速响应、快速迭代吗?然而最近一年的嵌入式领域经历......
  • bug解决日记
    bug解决日寄linux系统篇1、crontab设置定时开启失效crontab-e设置定时任务,如*****[commond]crontab-l查看定时任务,类似cat日志可以在var/log/cron或者/var/log/syslog/看到可能原因:1.命令本身问题,如权限不足等。2.如果命令在手动执行无误,看看不用shell脚本形......
  • 开发之调试-debug
    流程需求或创意--开发--调试--测试--部署-上线debugc++有gdbjava有jdbpython有pdb调试器会附加到应用进程打断点python调试printassertpdb##assert的异常参数,其实就是在断言表达式后添加字符串信息,用来解释断言并更好的知道是哪里出了问题。格式......
  • Advanced .Net Debugging 2:CLR基础
    一、简介这是2024新年后我的第一篇文章,也是我的《Advanced.NetDebugging》这个系列的第二篇文章。这篇文章告诉我们为了进行有效的程序调试,我们需要掌握哪些知识。言归正传,无论采取什么形式来分析问题,对被调试系统的底层了解的越多,就越有可能成功的找出问题的根源。在N......
  • ShowMeBug X MOMA 猛玛 | 提质增效两不误,以技术驱动高效招聘
    ShowMeBug与深圳市昊一源科技有限公司(以下简称昊一源)成功完成签约,并针对其技术人才需求提供全面的解决方案,助力昊一源汇聚优秀技术人才,构建高效招聘流程体系,为昊一源招聘效能的提升和优化奠定坚实基础。依托于ShowMeBug岗位题库和智能组卷功能,昊一源的HR只需要输入岗位名称与岗......
  • ShowMeBug 签约国内领先的金融支付技术服务商——新国都
    深圳市新国都股份有限公司(以下简称新国都)与ShowMeBug完成签约。针对其技术招聘提出解决方案,推动新国都的技术人才沉淀,构建专业的招聘流程闭环,为新国都带来招聘效能的提升和改进。在面试过程中,新国都通过使用ShowMeBug技术测评平台,提升人才选拔的效率与质量。ShowMeBug基于云端I......
  • 从一次DEBUG体会modernCPP
    今天写树形dp,用匿名函数出现了极其玄学的报错,modernCPP给我上了一课note:'solve()::<lambda(auto:23,int,int)>::~()'isimplicitlydeletedbecausethedefaultdefinitionwouldbeill-formed源代码voidsolve(){intn;std::cin>>n;std::vector<i......
  • GCC优化debug
    GCC编译器提供了多种优化选项,可以帮助改善代码的性能和效率,但当优化等级设置不对时,会导致coredump问题,本文对个人理解和网络上对GCC总结比较完善的文档做个记录备份,侵权联删GCC优化出现的问题个人分析思路:1、通过addr2line、nm等方法分析出现堆栈对应的代码段,分析代码逻辑,若代码......
  • 第2单 - 实验 debug 命令
         编写命令 以汇编方式向内存中写入数据 A命令 向0010:0000 地址写入汇编  U命令查看刚录入的命令 查看内存中的汇编语言 U命令   修改CS,IP,到新录入的程序   T命令 执行  2.    3. Dff......
  • [转帖]AMD首次公布Zen4c频率!果然不是“小核心”
    https://www.mydrivers.com/ 快科技1月22日消息,Intel大小核混合架构采用了截然不同的架构和规格,小核无论频率还是技术特性都差得远,AMDZen4、Zen4c的组合虽然也可以算作大小核,但后者只是精简了三级缓存容量、降低了频率,本质上并无差异。正因为如此,AMD一再强调,Zen4c并不是小......