生产rabbitMq消费者莫名其妙消失

生产rabbitMq消费者莫名其妙消失

原因: 业务方发现2个系统数据一致性不对,立马就联系了我们技术人员,我们这两个系统通过 MQ 进行解耦的。所以第一时间是查看消息是否积压。发现mq中有500多条消息,数量不多,但是为什么没有消费那?仔细一看,这个队列没有消费者!

那么问题来了?同一个应用,其他队列的消费者都还在,只有这个队列的消费者丢失。

第一时间重启应用不要耽误业务时间,重启后查看日志发现消费者在一直反复的消费第一条消息,然后又消失了。发现日志里存在大量的 \  ,数据应该是正常的 json串。消费者线程消失的原因就是这里了,消息不正确一直消费不了。

查看应用打印的日志发现问题: 因为消息太大了,导致 OOM  消费者线程中断了。rabbitMq 消费者小时的原因找到了。

o.s.a.r.l.SimpleMessageListenerContainer Line:1636 1636 - Consumer thread error, thread abort.
java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:649)
	at java.lang.StringBuilder.append(StringBuilder.java:202)
	at org.apache.http.client.utils.URLEncodedUtils.format(URLEncodedUtils.java:357)
	at org.apache.http.client.utils.URLEncodedUtils.format(URLEncodedUtils.java:334)
	at org.apache.http.client.entity.UrlEncodedFormEntity.<init>(UrlEncodedFormEntity.java:58)

新问题来了 

为什么?原本正常的json串会夹杂那么多的  \ ?

通过仔细查看消息属性发现,正常的消息属性  这里应该是 user类,但是带 \ 的消息 类型都是String 

正确的

错误的

这时候就可以肯定,这个消息不是上层系统发过来的,那么这个队列的消息还有其他生产者吗?

 

 @RabbitListener(queues = StaticConstant.WRITE_BACK_SCAN_FINISH_QUEUE,containerFactory = "sendScanInfoToOmsContainerFactory")
    public void onMessage(Message message, Channel channel) throws Exception {
        String body = "";
        try {
            body = new String(message.getBody(),"utf-8");
            // 正常业务逻辑
        } catch (Exception e) {
            // 如果出现异常重新丢回队列
            if(!StringUtils.isEmpty(body)){
              rabbitTemplate.convertAndSend(body);
            }
        }
    }

通过仔细检查代码发现,接受到的 body 又被丢到队尾了。这时已经可以知道了,因为他直接用了 rabbitTemplate重新丢的,rabbitTemplate 使用的 Jackson2JsonMessageConverter 序列化处理器。json处理String的时候回自动在两边加 " " , 因为之前的map转的json里面已经带有 “ 了,这时候会对body字符串里的 “ 进行转义处理。 

 

举个例子:

 一个简单的例子,新建一个map 然后转成json串,模拟成我们MQ 中的body,然后我们接受到,再使用toJsonString 再序列化一次

   public static void main(String[] args) {
        HashMap<Object, Object> hashMap = Maps.newHashMap();
        hashMap.put("A", 1);
        hashMap.put("B", 2);
        hashMap.put("C", 3);
        String body = JSON.toJSONString(hashMap);
        System.out.println("body====="+body);
        System.out.println("body序列化之后====="+JSON.toJSONString(body));
    }

控制台

body====={"A":1,"B":2,"C":3}
body序列化之后====="{\"A\":1,\"B\":2,\"C\":3}"

查看控制台结果,和分析一致。

因为消息一致循环被丢到队尾导致后面转移字符成倍增长,很快消费者就会被卡掉。

查看json源码 

com.alibaba.fastjson.serializer.SerializeWriter#write('"'); 在前后分别加了,通过设置可以改为单引号 '  

还是用之前的例子实验一下,用单引号可以暂时解决问题。

控制台输出:

body====={"A":1,"B":2,"C":3}
body序列化之后====='{"A":1,"B":2,"C":3}'

解决方法

既然我们知道是序列化的问题了,线上继续解决问题,我采用 反序列化方法。逻辑很简单,具体看代码

//body是消息体
// 临时处理 线上问题
while (body.contains("\\\\")){
      body= com.alibaba.fastjson.JSON.parseObject(body, String.class);
 }

body= com.alibaba.fastjson.JSON.parseObject(body, String.class);

因为我们的消息体中,本身消息文本可能存在 \ ,所以while条件使用 两个 \\,然后循环出来,再手动反序列化一次,就可以处理成原始正常数据  

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值