场景描述:
临下班时,突然接到电话。说某个客户的生产环境上,MQ中断,不能再接收数据,导致整个业务流程中断。由于临下班本来就是一个业务操作的高峰期,造成大面积的客户,门店等待。
通过了解情况,客户环境通过MQ进行消息中转,解决核心业务系统与我方系统的解藕。我方系统为四个tomcat的集群。
查看MQ的console,消息量一直在持续增加,其他的消息目标仍有消费者。由此推出MQ服务器本身应该没有问题。
根据前方的同事的反馈,重启业务系统,MQ上仍然没有消费者。同时反馈中,数据库上有死锁。日志上有标题上的空指针异常。详细如下:
java.lang.NullPointerException at org.aspectj.weaver.reflect.ShadowMatchImpl$RuntimeTestEvaluator.visit(ShadowMatchImpl.java:140) at org.aspectj.weaver.ast.Instanceof.accept(Instanceof.java:29) at org.aspectj.weaver.reflect.ShadowMatchImpl$RuntimeTestEvaluator.matches(ShadowMatchImpl.java:121) at org.aspectj.weaver.reflect.ShadowMatchImpl.matchesJoinPoint(ShadowMatchImpl.java:78) at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:313) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:159) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at $Proxy24.save(Unknown Source)
于是乎,怀疑解决死锁是因为有四台同时接收MQ消息。于是关掉三台业务系统的MQ接收,同时在去掉死锁后,重启业务系统,发布可以正常接收MQ消息了。虽然不能理解数据库的死锁跟MQ的中断有什么关系。同时,日志中的NullpointException又有什么关系?
正当高兴问题已经解决时,前方又反馈MQ上对应的消费者又丢失了。
于是按日志上的NullpointException去google了一下。这个明显是spring内部的问题,会跟数据有关系吗?
打到以下bug记录(https://bugs.eclipse.org/bugs/show_bug.cgi?id=318899)。即在spring2.5对应的切面实现中,aspectj对函数第一个参数为空值的情况有bug。会抛出上面提到的异常。顺势查找接口代码,对于外部消息的主键查找本地记录时,没有判断是否存在,都进行save操作。添加判断后,此问题解决。
还原错误过程:
一、首先系统MQ接收接口编写不严谨。
1、没有使用连接池(PooledConnectionFactory)。这样,接收过程由于网络或其他原因中断连接后,不能再重新连接MQ服务器,建立消费者关系。
2、消息接收自动AUTO_ACK模式,于是接收过程有异常情况,不能正常处理消息的情况下。MQ服务器的消息已经被接收下来并自动给予了ACK回执。有可能造成消息丢失。
二,业务接收代码编写不严谨,没有进行合理的判断。
三,为什么在MQ上的消费者会消失?
虽然在onMessage里,代码有进行try catch异常。但因为数据问题及程序没有合理判断,同时spring中AspectJ的bug。应该是由于NullException进而引发Error抛出,不能被拦截。所有连接失效。
四,为什么重启,有时候有效有时候无效?
因为前面提到的编写不严谨,消息一接收就从MQ上进行删除。所有一条错误数据的消息接收一次就消失了,刚好下一条消息是正常的,则重启成功了。如果下一条消息也是错误,不存在的数据,那么tomcat重启后,实际是有建立MQ的消费者的,只是一接收马上又挂了。等我们去console查看时,早已显示无MQ消费者接收了。
五,假设第一条MQ编写严谨,而第二点依然没有进行业务判断,结果如何?
原系统中,存在一定的将错就错。如果只修改第一条,而第二条不改的话,将造成无限死循环。因为错误结果的消息一直收不到ACK,就直存在于MQ服务器中。