记一次正则表达式引发的 stackoverflow 惨案
记一次正则表达式引发的 stackoverflow 惨案
1、业务背景
现有某产品需求,在系统收到了消息后,要对消息的文本内容进行一定规则的过滤,符合规则的丢弃,其他方可入库。具体的规则如下:
- 单个文本(单个数字、单个汉字)
- 纯数字文本
- 纯表情(只能识别微信官方的表情)
我们有一个节点监听了 Kafka 的消息 topic,所以该功能是在处理消息时实现。
2、问题现象
2022 年 7 月 7 日上午,突然收到了暴雷的反馈,这个功能上线了 10 天左右,第一次出现该问题:


3、排查过程
收到反馈后,我快马加鞭的打开了项目,定位了相关的代码,如下:


按照我们 JVM 的知识,StackOverflow 是由栈(方法)引发的异常,可能存在死循环或递归调用等;但从上面的代码中可以看出,表面上并没有出现循环或者递归的逻辑。此时,领导提到的 Pattern 类则慢慢映入眼帘……
当我打开 baidu.com,输入 java pattern 的时候,天了噜!竟然出现了这样的提示,我就知道事情没这么简单:

顺着这些蛛丝马迹,大概率猜测到是 WECHAT_EMOTICON 表达式里的分组语法在作祟。
4、定位原因
Java 的 Pattern 会通过递归的方式去匹配,其一直就存在着一个已知的会导致死循环的 bug,而且这个 bug 是 won't fix 的状态,详见:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507
目前猜测是 WECHAT_EMOTICON 表达式里的语法太长,当匹配次数过多的时候,栈的调用深度就会特别深,直至导致 overflow。
利用排除法,先把该表达式屏蔽掉,若后续不出现问题,则是该原因实锤。
5、解决方案
因为已经积攒了 30w+ 的消息数据,且该表情识别并不重要,遂紧急修复,把该表情识别的代码注释掉了,上线后暂时恢复正常。
6、总结经验
对于 Java 的正则表达式,一直流传着一句老话:“你有一个问题,用正则表达式解决了。现在,你有两个问题了。”
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
正则表达式是把双刃剑,在大规模数据校验或者高并发的应用中,不要轻易使用,效率非常差。

