在解析Web访问日志时,需要将机器人与人类用户代理分开。
我手动构建了一个列表,提取了类似bot的模式(例如包含 bot )。我有一个模式列表(约300项大)我实时验证传入的访问日志。随着时间的推移,这已经成为一个瓶颈,因为我的(低效)算法寻找任何模式;即使我们忽略了性能下降,这意味着对DoS的漏洞。
String userAgent;
List<Pattern> botPatterns;
for (Pattern botPattern : botPatterns)
{
if (botPattern.matcher(userAgent).find())
{
return true;
}
}
return false;
这可能是一个简单的解决方案,但结果有时是不准确的。现在想避免使用内置解决方案。
通过在hashmap中存储用户代理字符串,可以缓存结果。但是,即使这会提高性能,也会增加使用随机生成的用户代理字符串进行简单DoS攻击的漏洞。
是否有一种有效的算法可以将大量(N)字符串与固定数量的正则表达式(m)进行匹配?
我使用实际数据以及随机生成的用户代理字符串对@stemm的答案进行了基准测试。我在10秒内运行测试,平均计算时间。 缓存是一个大小为5000的简单LFU ehcache
以下是我的结果:
真实数据:101us
随机数据:193us
带有缓存的真实数据:4us
随机数据:203us
真实数据:100us
随机数据:160us
带有缓存的真实数据:3us
随机数据:154us
感谢大家的回答和评论。
答案 0 :(得分:1)
如果你有固定数量的模式,你可以构建Finite State Machine来表示你的模式,这可以大大提高性能。但是这个解决方案需要额外的逻辑开发和代表FSM。
示例您有多个模式[abc,abd,acd,dab]
有限状态机看起来像:
start -> a -> b -> c -> finish
-> d -> finish
-> c -> d -> finish
-> d -> a -> b -> finish
您可以将FSM视为状态有限的有序图。您希望在这些模式上匹配字符串abd
。从一开始就遇到一个 - &gt; b - &gt; d并完成。这意味着字符串匹配您可以从路径还原的模式。具有*
和?
等功能的更复杂模式可以表示链接状态与自身(*
),或者完全跳过此状态(?
)。
答案 1 :(得分:1)
主要想法是创建聚集模式,其中包含您拥有的所有正则表达式。
例如 - 您希望找到与这些正则表达式相匹配的字符串:^\d+$
或^[aA]+$
或^[a-z\s]+$
。因此,您可以将它们组合成这样的正则表达式:(^\d+$|^[aA]+$|^[a-z\s]+$)
示例java代码:
public class Test {
public static Pattern gather( String... patterns ) {
StringBuilder finalRegexBuilder = new StringBuilder( "(" );
for ( String ptr : patterns ) {
finalRegexBuilder.append( ptr ).append( "|" );
}
finalRegexBuilder.setLength( finalRegexBuilder.length() - 1 );
finalRegexBuilder.append( ")" );
return Pattern.compile( finalRegexBuilder.toString() );
}
public static void main( String[] args ) {
Pattern regex = gather( "^\\d+$", "^[aA]+$", "^[a-z\\s]+$" );
System.out.println( regex.toString() ); // (^\d+$|^[aA]+$|^[a-z\s]+$)
System.out.println( regex.matcher( "1235" ).find() ); // true
System.out.println( regex.matcher( "1235fdjg" ).find() ); // false
System.out.println( regex.matcher( "AAAaaaAaa" ).find() ); // true
System.out.println( regex.matcher( "hello world" ).find() ); // true
}
}
实际上 - 正则表达式的底层机制是有限状态机。所以,在上面的代码中我们得到了@mishadoff谈到的FSM。