日期:2014-05-16 浏览次数:20892 次
? 距离上一篇文章已经有4个多月了,这4个多月一直在忙着做一个数据库同步产品的代码研发和测试,现在基本运行稳定。 本文主要介绍一下,当时使用apache oro包进行正则过滤时,使用时出现的一个并发问题,排查了好几天才找到原因。希望大家使用时引以为戒,望周知。
?
简单的描述下,我使用apache oro的场景: 进行数据库同步时,我们会根据定义的表名进行匹配,从binlog的数据流中提取出我们关心的表,然后进行解析,压缩,传输,写入目标库等一系列动作。?
然而在线下测试环境中,冒出一个比较异常的情况,数据没有被正常的同步到目标库(概率发生的比较小,每次jvm重启后才可能出现),一节一节的往上查,最后定位是正则匹配时出的问题。
?
?
出问题的代码: (这是当时出问题的代码,犯了多个错误)
?
public class RegexFunction extends AbstractFunction { private Map<String, Pattern> patterns = null; private final PatternCompiler pc = new Perl5Compiler(); public RegexFunction(){ patterns = new MapMaker().softValues().makeComputingMap(new Function<String, Pattern>() { public Pattern apply(String pattern) { try { return pc.compile(pattern, Perl5Compiler.CASE_INSENSITIVE_MASK); } catch (MalformedPatternException e) { throw new CanalSinkException(e); } } }); } public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) { String pattern = FunctionUtils.getStringValue(arg1, env); String text = FunctionUtils.getStringValue(arg2, env); Perl5Matcher matcher = new Perl5Matcher(); boolean isMatch = matcher.matches(text, patterns.get(pattern)); return AviatorBoolean.valueOf(isMatch); } public String getName() { return "regex"; } }
?
1. ?Perl5Compiler是有状态的,会在compiler过程中产生一些上下文状态。 (之前先入为主的认为类似于Compiler基本都是单例使用,线程安全,基本我自己写代码和命名是这个习惯)
?
对应的Perl5Compiler的javadoc: ?(已经比较明确的指出,Perl5Compiler和Perl5Matcher是非线程安全的)
?
public class MutliAviaterFilterTest { @Test public void test_simple() { int count = 5; ExecutorService executor = Executors.newFixedThreadPool(count); final CountDownLatch countDown = new CountDownLatch(count); final AtomicInteger successed = new AtomicInteger(0); for (int i = 0; i < count; i++) { executor.submit(new Runnable() { public void run() { try { for (int i = 0; i < 100; i++) { doRegexTest(); // try { // Thread.sleep(10); // } catch (InterruptedException e) { // } } successed.incrementAndGet(); } finally { countDown.countDown(); } } }); } try { countDown.await(); } catch (InterruptedException e) { } Assert.assertEquals(count, successed.get()); executor.shutdownNow(); } private void doRegexTest() { AviaterRegexFilter filter3 = new AviaterRegexFilter("otter2.otter_stability1|otter1.otter_stability1|" + RandomStringUtils.randomAlphabetic(200)); boolean result = filter3.filter("otter1.otter_stability1"); Assert.assertEquals(true, result); result = filter3.filter("otter2.otter_stability1"); Assert.assertEqua