日期:2014-05-16 浏览次数:21258 次
? 距离上一篇文章已经有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