药丸!我往服务器里丢了20w垃圾数据

219 阅读3分钟

记第一次比较大的失误

2021年11月24日,基本解决了壮壮写的bug后,壮壮决定记录下这第一次工作写的大bug。

背景:上周五下班前,壮壮接到一个任务,写一个定时任务,定时检索推送失败的消息,周末加班是不可能滴,所以,这周一我才开始搞这个事,在我一通敲之后,方法完成了,语句优美,考虑周到,平均俩行一个注释,作为一个新人,我觉得很不错。问了问大哥,大哥说那你就测吧,不过其它服务在集群上,你把你的部署上去测一下。我就部署上去,然后看到快55分了,就把定时设置在了55分。数着秒到了55,日志开始疯狂的出现,我一惊,完蛋,我就整了3条带壮壮名字的假数据啊,然后快速的删掉了我的服务,整个过程不到1分钟。删掉之后,我想仔细排查问题,然后突然发现消息服务疯狂被访问,问了下大哥,大哥找了找,通道服务一直在进行请求,他找了很久发现我那个定时任务不到1分钟居然往消息队列里塞了20w数据,每一条都是壮壮消息重推测试,我脑门瞬间冒冷汗,啊,这。。。

代码:

public void resendMessage(String id) {
        //这里while(true)是因为每次只处理100条,
        //所以处理完就再去查100条进行处理,最后查不到就证明全部处理完了
        while (true) {
            //通过Id获取需要重新推送的消息,limit 100
            List<Message> messageList = messageRepository.findByAppId(appId);
            //判断需要重推的消息列表是否为空
            if (CollectionUtils.isEmpty(messageList)) {
                //无待处理的消息,跳出循环
                break;
            }
            //遍历需重推的消息列表
            for (Message message : messageList) {
                //获取重试次数
                int tries = Optional.ofNullable(notice.getTries()).orElse(0);
                try {
                    //同步发送消息,失败抛出异常
                    producer.syncSend(message);
                    log.info(message.getMessageId() + " resend ok.");
                    //更新message信息(问题出现的地方)
                    messageRepository.updateProcessResult(result);
                } catch (ExecutionException|InterruptedException e) {
                    //发送失败的处理
                    //设置处理状态为待处理,无关代码,删掉
                }
            }
        }
    }

问题

  • 最初找问题的时候,发现是重推成功后,处理状态不发生改变造成的。定位后,发现更新的接口有个id必传,但是我没有传(这个接口的调用是从原消息推送方法里copy过来的,我没想到它有bug,还漏传参数,也怪我不够细心,都没看一眼接口就调用)

  • 修改方案: 必然是传上那个缺失的id

  • 因为后果比较严重,还是我毕业后遇到的第一次重大失误,所以今天我进行复盘,我本来以为原因是,更新失败异常,但是下面的catch 指定了发送消息的俩个异常,所以没有捕获更新失败的异常,但是因为在 try 里,所以它没有抛出异常。写了个小例子试了试,是我无知了,不捕获是可以正常抛出的

  • 那么问题在哪里呢

  • 我再次进入那个更新查看,发现它的传参是一个对象,对象里有那个id,所以 mybatis 会拼接一个 null 上去,然后我就去执行了这个 sql ,更新成功,更新了0行,原来如此,排查结束。

    UPDATE message 
    SET process_status = 0 
    WHERE
        app_id = NULL 
        AND message_id = 1
    ​
    > Affected rows: 0
    > 时间: 0.004s
    

总结

  • 年轻人不要不知天高地厚,不经测试就 while(true) ,还上服务器是不行的,昨天改完再测的时候,我是从循环3次到10次,最后才改成true的,希望我能牢记教训,以后都先 限制循环次数,测试没问题再 while(true)
  • 调用接口,一定要看看接口的要求,哪些参数是必传
  • 随手粘来的代码一定要研究透,不然出问题都不知道去哪里找(这还是在本项目里复制的,要是百度的可能更容易出问题)