apache oro使用注意细节(并发问题)

背景

  距离上一篇文章已经有4个多月了,这4个多月一直在忙着做一个数据库同步产品的代码研发和测试,现在基本运行稳定。 本文主要介绍一下,当时使用apache oro包进行正则过滤时,使用时出现的一个并发问题,排查了好几天才找到原因。希望大家使用时引以为戒,望周知。

 

过程

简单的描述下,我使用apache oro的场景: 进行数据库同步时,我们会根据定义的表名进行匹配,从binlog的数据流中提取出我们关心的表,然后进行解析,压缩,传输,写入目标库等一系列动作。 

然而在线下测试环境中,冒出一个比较异常的情况,数据没有被正常的同步到目标库(概率发生的比较小,每次jvm重启后才可能出现),一节一节的往上查,最后定位是正则匹配时出的问题。

 

原因

 

出问题的代码: (这是当时出问题的代码,犯了多个错误)

1.public class RegexFunction extends AbstractFunction {
2.
3.    private Map<String, Pattern>  patterns = null;
4.    private final PatternCompiler pc       = new Perl5Compiler();
5.
6.    public RegexFunction(){
7.        patterns = new MapMaker().softValues().makeComputingMap(new Function<String, Pattern>() {
8.
9.            public Pattern apply(String pattern) {
10.                try {
11.                    return pc.compile(pattern, Perl5Compiler.CASE_INSENSITIVE_MASK);
12.                } catch (MalformedPatternException e) {
13.                    throw new CanalSinkException(e);
14.                }
15.            }
16.        });
17.    }
18.
19.    public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
20.        String pattern = FunctionUtils.getStringValue(arg1, env);
21.        String text = FunctionUtils.getStringValue(arg2, env);
22.        Perl5Matcher matcher = new Perl5Matcher();
23.        boolean isMatch = matcher.matches(text, patterns.get(pattern));
24.        return AviatorBoolean.valueOf(isMatch);
25.    }
26.
27.    public String getName() {
28.        return "regex";
29.    }
30.
31.}

说明:

 

  1. 使用了aviator做为表达式引擎( http://code.google.com/p/aviator/),扩展了一个regex表达式语法的支持。(默认是自带了jdk pattern的正则)
  2. 使用google collection,将pattern进行lazy-init处理,多线程共享。(map中没有对应pattern,就进行compiler)

错误1:  

1.  Perl5Compiler是有状态的,会在compiler过程中产生一些上下文状态。 (之前先入为主的认为类似于Compiler基本都是单例使用,线程安全,基本我自己写代码和命名是这个习惯)

 

对应的Perl5Compiler的javadoc:  (已经比较明确的指出,Perl5Compiler和Perl5Matcher是非线程安全的)

 

我做了个测试:

1.public class MutliAviaterFilterTest {
2.
3.    @Test
4.    public void test_simple() {
5.        int count = 5;
6.        ExecutorService executor = Executors.newFixedThreadPool(count);
7.
8.        final CountDownLatch countDown = new CountDownLatch(count);
9.        final AtomicInteger successed = new AtomicInteger(0);
10.        for (int i = 0; i < count; i++) {
11.            executor.submit(new Runnable() {
12.
13.                public void run() {
14.                    try {
15.                        for (int i = 0; i < 100; i++) {
16.                            doRegexTest();
17.                            // try {
18.                            // Thread.sleep(10);
19.                            // } catch (InterruptedException e) {
20.                            // }
21.                        }
22.
23.                        successed.incrementAndGet();
24.                    } finally {
25.                        countDown.countDown();
26.                    }
27.                }
28.            });
29.        }
30.
31.        try {
32.            countDown.await();
33.        } catch (InterruptedException e) {
34.        }
35.
36.        Assert.assertEquals(count, successed.get());
37.        executor.shutdownNow();
38.    }
39.
40.    private void doRegexTest() {
41.        AviaterRegexFilter filter3 = new AviaterRegexFilter("otter2.otter_stability1|otter1.otter_stability1|"
42.                                                            + RandomStringUtils.randomAlphabetic(200));
43.        boolean result = filter3.filter("otter1.otter_stability1");
44.        Assert.assertEquals(true, result);
45.        result = filter3.filter("otter2.otter_stability1");
46.        Assert.assertEquals(true, result);
47.    }
48.
49.}

 说明:

  1. 每次构造一条正则(固定的字符串 + 一段200个字符的随机值)
  2. 多线程并发的进行compiler ,  matcher操做 

结果:

 

  1. 单线程测试顺利通过
  2. 多线程测试,没加200字符的随机值,尝试到20,30并发都没有发现failed
  3. 多线程测四和,正则表达式加上200字符的随机值,尝试2个并发就遇到了failed情况

看来需要有足够的碰撞才行,通过增加表达式长度,增加了compiler的编译时间,增加了多线程碰撞的几率,从而造成编译出的Pattern是一个不正确的状态机。

错误2: 

2. Pattern在不同的参数编译下,会有不同的结果,也会有并发问题。 (对应javadoc中也已经有了说明)

 

 

我也做了个类似测试,在20,30的并发度下,也没有发现failed情况。后续可以在更高的压力,更复杂的正则表达式进行匹配,估计碰撞的概率就会比较高

 

总结

正确的代码写法:

 

1.public RegexFunction(){
2.        patterns = new MapMaker().softValues().makeComputingMap(new Function<String, Pattern>() {
3.
4.            public Pattern apply(String pattern) {
5.                try {
6.                    <span style="color: rgb(255, 0, 0);">PatternCompiler pc = new Perl5Compiler();</span>
7.                    return pc.compile(pattern, Perl5Compiler.CASE_INSENSITIVE_MASK<span style="color: rgb(255, 0, 0);"> | Perl5Compiler.READ_ONLY_MASK</span>);
8.                } catch (MalformedPatternException e) {
9.                    throw new CanalSinkException(e);
10.                }
11.            }
12.        });
13.    }

看来在使用一些三方库时要多注意一些细节,尽管你之前已经对该三方库使用已经比较久,但未必不会出问题,只不过是你的运气好与不好而已。 

 

时间: 2024-11-01 04:32:12

apache oro使用注意细节(并发问题)的相关文章

Caused by: java.lang.ClassNotFoundException: org.apache.oro.text.regex.Malformed

问题描述 215 UNIX Type: L8Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/oro/text/regex/MalformedPatternException at org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory.createUnixFTPEntryParser(DefaultFTPFileEnt

linux中Apache使用mod_limitipconn模块限制并发

mod_limitipconn模块下载 http://dominia.org/djao/limitipconn2.html Linux环境的安装 如果您需要手动安装mod_limitipconn,请按照下面的说明.下面的说明是主要应用于Apache 2.2.9,但它也支持任何更高版本的Apache. 编译到httpd的mod_limitipconn说明 tar xzvf httpd-2.2.9.tar.gztar xjvf mod_limitipconn-0.23.tar.bz2cd httpd

查看Apache并发请求数及其TCP连接状态

[文章作者:张宴 本文版本:v1.1 最后修改:2007.07.27 转载请注明出处:http://blog.s135.com] 这两天搭建了一组Apache服务器,每台服务器4G内存,采用的是prefork模式,一开始设置的连接数太少了,需要较长的时间去响应用户的请求,后来修改了一下Apache 2.0.59的配置文件httpd.conf: # prefork MPM# StartServers: number of server processes to start# MinSpareSer

Apache的压力测试工具及OB缓存

apache在做压力测试这方面还是特别方便的,apache里就直接由这样的工具而且使用方便,在这之前我们需要了解apache使用的是那种并发机制 1.查看apache使用的是那种并发模型 可以直接通过cmd进入apache的bin目录 ,使用httpd.exe -l即可 找到mpm,后面的winnt就是并发模型 2.apache的默认并发数 apache的默认并发数是150,tomcat6的并发接近200,一般情况下apache使用默认的并发模型,从上面第一点我们知道了改apache的并发模型是

代理服务器 详解 Apache与Nginx的比较与分析

正向代理:是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端.客户端必须要进行一些特别的设置才能使用正向代理. 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器.

APACHE,NGINX 详细分析

Apache是目前最流行的Web应用服务器,占据了互联网应用服务器70%以上的份额.Apache能取得如此成功并不足为奇:它免费.稳定且性能卓越:但Apache能取得如此佳绩的另一个原因是,当时互联网刚刚兴起时,Apache是第一个可用的Web应用服务器,人们没有其他的选择. 不可否认,Apache是一个优秀的全能Web服务器,但对于那些需要更强大的Web应用服务器(比如大小.可定制.响应速度.可扩展性等方面)的人而言,Apache明显不符合他们的要求,寻找Apache的替代者是更好的选择. N

《深入理解Elasticsearch(原书第2版)》一第1章Elasticsearch简介1.1 Apache Lucene简介

第1章 Elasticsearch简介 我们希望读者通过阅读本书来获取和拓展关于Elasticsearch的基本知识.假设读者已经知道如何使用Elasticsearch进行单次或批量索引创建,如何发送请求检索感兴趣的文档,如何使用过滤器缩减检索返回文档的数量,以及使用切面/聚合(faceting/aggregation)机制来计算数据的一些统计量.不过,在接触Elasticsearch提供的各种令人激动的功能之前,希望读者能对Apache Lucene有一个快速的了解,因为Elasticsear

在Apache服务器上利用Varnish优化移动端访问的方法_Linux

想象一下,你刚刚发布了一篇博文,并分享到了社交网络.然后,这篇文章恰巧被大V看中再次分享了出去,立即吸引了数百粉丝的目光,引导他们涌入了你的网站.看到这么多的访客量,以及它们的评论,你内心激动不已.突然之间,你的网站就挂掉了,满屏的数据连接错误-- 或者假想另一种情境,你一直很努力地创业.突然有一天,一个大V在社交网络表达了对贵公司的喜爱之情,字里行间满满的赞叹.关注这个大V的粉丝心动了,又涌入了你的网站.不幸的是,点击连接后却无法进入你的网站,或者进入后无法注册用户,甚至页面相应超时,无法获取

深入Apache与Nginx的优缺点比较详解_php技巧

1.nginx相对于apache的优点: 轻量级,同样起web 服务,比apache占用更少的内存及资源 抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能 高度模块化的设计,编写模块相对简单 社区活跃,各种高性能模块出品迅速啊 apache 相对于nginx 的优点: rewrite ,比nginx 的rewrite 强大 动态页面 模块超多,基本想到的都可以找到 少bug ,nginx 的bug 相对较多  超稳定 存在就