Flume拦截器抛出异常导致数据错乱问题

标签(空格分隔): Flume


现象

在Flume插件包在某个生产环境上线之后出现以下问题:
Flume拦截器抛出异常导致数据错乱问题 - 图1

这个业务由2个拦截器分别处理部分逻辑来完成,流程如下
Flume拦截器抛出异常导致数据错乱问题 - 图2

第一个拦截器DataValidatorInterceptor接收到的字符串格式应该是这样,由5部分通过空格分开

  1. SUBMIT 0 LMPA1KMBXJA430312 FORWARD {VID:f4394558-a25c-4767-abfe-76a0a10b73de,VTYPE:8ad621d5658a8aa30165a3163f113047,TIME:20190307172839,RECVTIME:20190307172840,SENDTIME:20190307172841,FLAG:2,TYPE:2,RESULT:1,FORWARD_ID:8ad621d561fad78b01622da7d6de1729,SPLIT:1,PACKET:IyMC/kxNUEExS01CWEpBNDMwMzEyAQFBEwMHERwnAQEDAQAAAAKkkA5/J29NAQ4LtwAAAgEBATtOW0+IOg6mJxUIAQEOfydvAF4AAV4PYQ9sD2wPbg9tD20PbA9qD20PbA9tD24PbA9qD2MPbA9sD2wPag9qD2sPaw9uD2oPbQ9rD2wPbA9tD2sPaw9pD2wPbA9tD20PYw9rD24PbQ9uD20PbQ9sD24Pbw9sD28PbQ9sD20Pbg9tD24PbQ9tD28Pbw9pD20PbQ9sD2wPaw9sD24PbA9sD28PcA9sD24PbQ9qD2sPbw9tD2sPbQ9vD2wPbw9sD20Pbg9uD2MPbw9uD20PbQ9rD20PbAkBAQAoNjY2NTY2NjU1NTU1NTY2NjY2NTY1NjY2NTY1NjY2NjY2NjY2NTY1NQYBAQ9wAQEPYQEBNgEENQcAAAAAAAAAAAAFAAYzcScB1Bl6wQ==}

但是从日志中看到DataValidatorInterceptor拦截器接收到的数据格式是json,这个格式是第二个拦截器处理过后的数据。

排查

  1. 初步怀疑,拦截器乱序,但是通过查看Flume源码,发现没有这个可能,因为Flume对于拦截器的处理是直接通过数组,循环来调用每个拦截器,是顺序处理的。
    Flume拦截器抛出异常导致数据错乱问题 - 图3

  2. 继续观察日志,看看有没有可疑的地方,结果找到以下内容
    Flume拦截器抛出异常导致数据错乱问题 - 图4

这个说明在Source或者Intercept阶段存在空指针异常,会不会跟这个有关系呢

  1. 添加日志,在拦截器中捕获所有异常,打印堆栈信息定位到是由于第二个拦截器中某个时间自动为空导致抛出空指针异常,但是一个异常为什么会引起拦截器错乱呢,继续扒源码,找到以下信息
    Flume拦截器抛出异常导致数据错乱问题 - 图5
    如果Source中出现异常,则会对该批次消息标记补偿,会重新执行,但是重新执行也不会导致第二个拦截器的数据被第一个拦截器获取到呀,继续扒
    Flume拦截器抛出异常导致数据错乱问题 - 图6
    承载批量消息的eventList是一个全局变量
    Flume拦截器抛出异常导致数据错乱问题 - 图7
    而在整个拦截器处理过程中,只会改变其中body的值,event的引用是不变的。
    Flume拦截器抛出异常导致数据错乱问题 - 图8

  2. 到这里基本就清晰了,当抛出异常之后进行消息补偿的时候,第一个拦截器重新拿到的内容不是原始内容,而是第二个拦截器处理过后的数据,因为这个过程都是改变的eventList中的数据,而eventList本身是没清空的

解决

对拦截器内部逻辑增加容错,捕捉异常,打印日志。

总结

警惕 Flume拦截器中不能抛出异常,否则导致多个拦截器错乱。
另外要注意程序中打印出的每个ERROR,就是因为一开始对于Source偶尔抛出的空指针异常没有重视才导致排查了很久