Event Ruler(为简洁起见,在文档的其余部分中称为 Ruler)是一个 Java 库,允许将规则与事件进行匹配。事件是字段列表,可以以名称/值对或 JSON 对象的形式给出。规则将事件字段名称与可能值的列表相关联。使用标尺有两个原因:
内容:
通过例子来解释是最容易的。
事件是一个 JSON 对象。这是一个例子:
{
"version" : "0" ,
"id" : "ddddd4-aaaa-7777-4444-345dd43cc333" ,
"detail-type" : "EC2 Instance State-change Notification" ,
"source" : "aws.ec2" ,
"account" : "012345679012" ,
"time" : "2017-10-02T16:24:49Z" ,
"region" : "us-east-1" ,
"resources" : [
"arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000"
] ,
"detail" : {
"c-count" : 5 ,
"d-count" : 3 ,
"x-limit" : 301.8 ,
"source-ip" : "10.0.0.33" ,
"instance-id" : "i-000000aaaaaa00000" ,
"state" : "running"
}
}
您还可以将其视为一组名称/值对。为简洁起见,我们仅提供一个示例。 Ruler 具有用于以 JSON 形式和名称/值对的形式提供事件的 API:
+--------------+------------------------------------------+
| name | value |
|--------------|------------------------------------------|
| source | "aws.ec2" |
| detail-type | "EC2 Instance State-change Notification" |
| detail.state | "running" |
+--------------+------------------------------------------+
JSON 形式的事件可以以原始 JSON 字符串或解析的 Jackson JsonNode 的形式提供。
本节中的规则均与上面的示例事件匹配:
{
"detail-type" : [ "EC2 Instance State-change Notification" ] ,
"resources" : [ "arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000" ] ,
"detail" : {
"state" : [ "initializing" , "running" ]
}
}
这会将任何事件与为resource
、 detail-type
和detail.state
值提供的值进行匹配,忽略事件中的任何其他字段。如果detail.state
的值是"initializing"
它也会匹配。
规则中的值始终以数组形式提供,并且如果事件中的值是数组中提供的值之一,则匹配。对resources
的引用表明,如果事件中的值也是数组,则如果事件数组和规则数组之间的交集非空,则规则匹配。
{
"time" : [ { "prefix" : "2017-10-02" } ]
}
前缀匹配仅适用于字符串值字段。
{
"source" : [ { "prefix" : { "equals-ignore-case" : "EC2" } } ]
}
前缀 equals-ignore-case 匹配仅适用于字符串值字段。
{
"source" : [ { "suffix" : "ec2" } ]
}
后缀匹配仅适用于字符串值字段。
{
"source" : [ { "suffix" : { "equals-ignore-case" : "EC2" } } ]
}
后缀 equals-ignore-case 匹配仅适用于字符串值字段。
{
"source" : [ { "equals-ignore-case" : "EC2" } ]
}
等于忽略大小写匹配仅适用于字符串值字段。
{
"source" : [ { "wildcard" : "Simple*Service" } ]
}
通配符匹配仅适用于字符串值字段。单个值可以包含零到多个通配符,但不允许连续的通配符。为了专门匹配星号字符,可以使用反斜杠转义通配符。两个连续的反斜杠(即用反斜杠转义的反斜杠)代表实际的反斜杠字符。不允许使用反斜杠转义星号或反斜杠以外的任何字符。
Anything-but 匹配顾名思义:匹配除规则中提供的内容之外的任何内容。
Anything-but 适用于单个字符串和数值或列表,其中必须完全包含字符串或完全数字。它还可以应用于字符串或字符串列表的前缀、后缀或等于忽略大小写匹配。
单个任何内容(字符串,然后是数字):
{
"detail" : {
"state" : [ { "anything-but" : "initializing" } ]
}
}
{
"detail" : {
"x-limit" : [ { "anything-but" : 123 } ]
}
}
除了列表之外的任何内容(字符串):
{
"detail" : {
"state" : [ { "anything-but" : [ "stopped" , "overloaded" ] } ]
}
}
除了列表(数字)之外的任何内容:
{
"detail" : {
"x-limit" : [ { "anything-but" : [ 100 , 200 , 300 ] } ]
}
}
除了前缀之外的任何内容:
{
"detail" : {
"state" : [ { "anything-but" : { "prefix" : "init" } } ]
}
}
任何前缀列表(字符串):
{
"detail" : {
"state" : [ { "anything-but" : { "prefix" : [ "init" , "error" ] } } ]
}
}
任何东西,但后缀:
{
"detail" : {
"instance-id" : [ { "anything-but" : { "suffix" : "1234" } } ]
}
}
任何后缀列表(字符串):
{
"detail" : {
"instance-id" : [ { "anything-but" : { "suffix" : [ "1234" , "6789" ] } } ]
}
}
任何情况但忽略大小写:
{
"detail" : {
"state" : [ { "anything-but" : { "equals-ignore-case" : "Stopped" } } ]
}
}
任何但忽略大小写的列表(字符串):
{
"detail" : {
"state" : [ { "anything-but" : { "equals-ignore-case" : [ "Stopped" , "OverLoaded" ] } } ]
}
}
除通配符外的任何内容:
{
"detail" : {
"state" : [ { "anything-but" : { "wildcard" : "*/bin/*.jar" } } ]
}
}
除通配符之外的任何内容列表(字符串):
{
"detail" : {
"state" : [ { "anything-but" : { "wildcard" : [ "*/bin/*.jar" , "*/bin/*.class" ] } } ]
}
}
{
"detail" : {
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ] ,
"d-count" : [ { "numeric" : [ "<" , 10 ] } ] ,
"x-limit" : [ { "numeric" : [ "=" , 3.018e2 ] } ]
}
}
上面,对c-count
、 d-count
和x-limit
的引用说明了数字匹配,并且仅适用于 JSON 数字的值。数字匹配支持与 Java 的double
原语相同的精度和范围,后者实现 IEEE 754 binary64
标准。
{
"detail" : {
"source-ip" : [ { "cidr" : "10.0.0.0/24" } ]
}
}
这也适用于 IPv6 地址。
存在匹配适用于 JSON 事件中是否存在字段。
下面的规则将匹配任何具有detail.c-count 字段的事件。
{
"detail" : {
"c-count" : [ { "exists" : true } ]
}
}
下面的规则将匹配任何没有detail.c-count 字段的事件。
{
"detail" : {
"c-count" : [ { "exists" : false } ]
}
}
注意Exists
匹配仅适用于叶节点。它不适用于中间节点。
作为一个例子,上面的例子中的exists : false
将匹配下面的事件:
{
"detail-type" : [ "EC2 Instance State-change Notification" ] ,
"resources" : [ "arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000" ] ,
"detail" : {
"state" : [ "initializing" , "running" ]
}
}
但也会匹配下面的事件,因为c-count
不是叶节点:
{
"detail-type" : [ "EC2 Instance State-change Notification" ] ,
"resources" : [ "arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000" ] ,
"detail" : {
"state" : [ "initializing" , "running" ]
"c-count" : {
"c1" : 100
}
}
}
{
"time" : [ { "prefix" : "2017-10-02" } ] ,
"detail" : {
"state" : [ { "anything-but" : "initializing" } ] ,
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ] ,
"d-count" : [ { "numeric" : [ "<" , 10 ] } ] ,
"x-limit" : [ { "anything-but" : [ 100 , 200 , 300 ] } ] ,
"source-ip" : [ { "cidr" : "10.0.0.0/8" } ]
}
}
如上面的示例所示,如果规则中命名的所有字段都匹配,则 Ruler 认为该规则匹配;如果提供的任何字段值匹配,则认为该字段匹配,也就是说 Ruler 应用了“与”逻辑默认情况下,所有字段都不需要“And”原语。
有两种方法可以达到“或”效果:
“$or”原语允许客户直接描述规则中字段之间的“或”关系。
仅当规则满足以下所有条件时,标尺才识别“或”关系:
/src/main/software/amazon/event/ruler/Constants.java#L38
中的 RESERVED_FIELD_NAMES_IN_OR_RELATIONSHIP 例如,以下规则将不会被解析为“或”关系,因为“数字”和“前缀”是标尺保留关键字。 {
"$or": [ {"numeric" : 123}, {"prefix": "abc"} ]
}
否则,Ruler 仅将“$or”视为普通字段名,与规则中的其他字符串相同。
正常的“或”:
// Effect of "source" && ("metricName" || "namespace")
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{ "namespace" : [ "AWS/EC2" , "AWS/ES" ] }
]
}
并行“或”:
// Effect of ("metricName" || "namespace") && ("detail.source" || "detail.detail-type")
{
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{ "namespace" : [ "AWS/EC2" , "AWS/ES" ] }
] ,
"detail" : {
"$or" : [
{ "source" : [ "aws.cloudwatch" ] } ,
{ "detail-type" : [ "CloudWatch Alarm State Change" ] }
]
}
}
“或”里面有一个“与”
// Effect of ("source" && ("metricName" || ("metricType && "namespace") || "scope"))
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
} ,
{ "scope" : [ "Service" ] }
]
}
嵌套“或”和“与”
// Effect of ("source" && ("metricName" || ("metricType && "namespace" && ("metricId" || "spaceId")) || "scope"))
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ] ,
"$or" : [
{ "metricId" : [ 1234 ] } ,
{ "spaceId" : [ 1000 ] }
]
} ,
{ "scope" : [ "Service" ] }
]
}
“$or”可能已经在某些应用程序中用作普通键(尽管可能很少见)。对于这些情况,Ruler 尽力保持向后兼容性。仅当满足上述 3 个条件时,统治者才会改变行为,因为它假设您的规则确实需要 OR 并且直到今天才配置错误。例如,下面的规则将继续作为正常规则工作,并将“$or”视为规则和事件中的正常字段名称:
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : {
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
}
}
有关“$or”被 Ruler 解析为普通字段名称的更多示例,请参阅/src/test/data/normalRulesWithOrWording.json
。
作为“Or”关系原语的关键字“$or”不应被设计为事件和规则中的普通字段。 Ruler 支持旧规则,其中“$or”被解析为普通字段名称,以保持向后兼容性,并为团队提供时间将旧的“$or”用法从事件和规则中迁移为普通字段名称。 Ruler 故意不支持混合使用“$or”作为“Or”原语和“$or”作为普通字段名称,以避免“$or”上出现超级尴尬的歧义。
有两种使用标尺的方法。您可以将多个规则编译为“机器”,然后使用其rulesForEvent()
方法或rulesForJSONEvent()
方法来检查哪些规则与任何事件匹配。下面讨论这两种方法之间的区别。本讨论将一般使用rulesForEvent()
除非差异很重要。
或者,您可以使用单个静态布尔方法来确定单个事件是否与特定规则匹配。
有一个静态布尔方法Ruler.matchesRule(event, rule)
- 两个参数都以 JSON 字符串形式提供。
注意:还有另一种已弃用的方法,称为Ruler.matches(event, rule)
,不应使用该方法,因为其结果与rulesForJSONEvent()
和rulesForEvent()
不一致。有关详细信息,请参阅有关Ruler.matches(event, rule)
的文档。
匹配时间不依赖于规则的数量。如果您有多个可能的规则想要从中进行选择,特别是如果您有办法存储已编译的 Machine,那么这是最佳选择。
匹配时间受到通配符和非通配符规则引起的不确定性程度的影响。随着越来越多的通配符规则前缀与理论上的最坏情况事件匹配,性能会下降。为了避免这种情况,与同一事件字段相关的通配符规则应避免导致第一个通配符的公共前缀。如果需要公共前缀,则使用最少数量的通配符,并限制通配符后面出现的重复字符序列。 MachineComplexityEvaluator 可用于评估机器并确定不确定性或“复杂性”的程度(即有多少通配符规则前缀与理论上的最坏情况事件匹配)。以下是一些数据点,显示随着复杂性分数的增加,性能通常会下降。
限制机器复杂性以保护您的应用程序非常重要。至少有两种不同的策略来限制机器的复杂性。哪一种更有意义可能取决于您的应用程序。
策略#1 更理想,因为它测量包含所有规则的机器的实际复杂性。如果可能,应使用此策略。缺点是,假设您有一个控制平面,允许一次创建一个规则,最多可以创建一个非常大的数量。然后,对于每个控制平面操作,您必须加载所有现有规则来执行验证。这可能非常昂贵。它还容易出现竞争条件。策略#2 是一种妥协。策略 #2 使用的阈值将低于策略 #1,因为它是每个规则的阈值。假设您希望添加所有规则后机器的复杂性不超过 300。例如,使用策略 #2,您可以将每个单规则机器的复杂性限制为 10,并允许包含通配符模式的 30 条规则。在复杂性完全相加(不太可能)的绝对最坏情况下,这将导致机器的复杂性为 300。缺点是复杂性不太可能完全相加,因此包含通配符的规则的数量将可能会受到不必要的限制。
对于策略#2,根据规则的存储方式,可能需要向规则添加附加属性以指示哪些规则是不确定的(即包含通配符模式),以便限制包含通配符的规则的数量。
下面的代码片段说明了如何限制给定模式的复杂性,例如策略#2。
public class Validate { private void validate ( String pattern , MachineComplexityEvaluator machineComplexityEvaluator ) { // If we cannot compile, then return exception. List < Map < String , List < Patterns >>> compilationResult = Lists . newArrayList (); try { compilationResult . addAll ( JsonRuleCompiler . compile ( pattern )); } catch ( Exception e ) { InvalidPatternException internalException = EXCEPTION_FACTORY . invalidPatternException ( e . getLocalizedMessage ()); throw ExceptionMapper . mapToModeledException ( internalException ); } // Validate wildcard patterns. Look for wildcard patterns out of all patterns that have been used. Machine machine = new Machine (); int i = 0 ; for ( Map < String , List < Patterns >> rule : compilationResult ) { if ( containsWildcard ( rule )) { // Add rule to machine for complexity evaluation. machine . addPatternRule ( Integer . toString (++ i ), rule ); } } // Machine has all rules containing wildcard match types. See if the complexity is under the limit. int complexity = machine . evaluateComplexity ( machineComplexityEvaluator ); if ( complexity > MAX_MACHINE_COMPLEXITY ) { InvalidPatternException internalException = EXCEPTION_FACTORY . invalidPatternException ( "Rule is too complex" ); throw ExceptionMapper . mapToModeledException ( internalException ); } } private boolean containsWildcard ( Map < String , List < Patterns >> rule ) { for ( List < Patterns > fieldPatterns : rule . values ()) { for ( Patterns fieldPattern : fieldPatterns ) { if ( fieldPattern . type () == WILDCARD || fieldPattern . type () == ANYTHING_BUT_WILDCARD ) { return true ; } } } return false ; } }
您将与之交互的主类实现基于状态机的规则匹配。有趣的方法是:
addRule()
- 向机器添加新规则deleteRule()
- 从机器中删除规则rulesForEvent()
/ rulesForJSONEvent()
- 查找机器中与事件匹配的规则有两种风格: Machine
和GenericMachine
。 Machine 就是GenericMachine
。 API 将泛型类型称为“名称”,这反映了历史:首先构建了 String 版本,它存储和返回的字符串被视为规则名称。
为了安全起见,用于“命名”规则的类型应该是不可变的。如果在将对象用作规则名称时更改对象的内容,则可能会破坏标尺的操作。
GenericMachine 和 Machine 构造函数可以选择接受 GenericMachineConfiguration 对象,该对象公开以下配置选项。
默认值: false 通常,如果先前已添加给定键子序列和模式,或者已经为给定键子序列添加了模式,则 NameState 会重新用于给定键子序列和模式。因此,默认情况下,NameState 重用是机会主义的。但通过将此标志设置为 true,将强制对关键子序列重用 NameState。这意味着,如果之前已添加过该键子序列,则为该键子序列添加的第一个模式将重新使用 NameState。这意味着每个键子序列都有一个 NameState。在某些情况下,这会以指数方式提高内存利用率,但确实会导致更多子规则存储在各个 NameState 中,Ruler 有时会对其进行迭代,这可能会导致运行时性能适度下降。为了向后兼容,此值默认为 false,但很可能,除了对延迟最敏感的应用程序之外,所有应用程序都将从将其设置为 true 中受益。
这是一个简单的例子。考虑:
machine . addRule ( "0" , "{"key1": ["a", "b", "c"]}" ) ;
模式“a”创建一个NameState,然后,即使additionalNameStateReuse=false,第二个模式(“b”)和第三个模式(“c”)也会重复使用相同的NameState。但请考虑以下几点:
machine . addRule ( "0" , "{"key1": ["a"]}" ) ;
machine . addRule ( "1" , "{"key1": ["b"]}" ) ;
machine . addRule ( "2" , "{"key1": ["c"]}" ) ;
现在,如果additionalNameStateReuse=false,我们最终会得到三个NameState,因为每个规则添加中关键子序列遇到的第一个模式将创建一个新的NameState。因此,“a”、“b”和“c”都有自己的 NameState。然而,当additionalNameStateReuse=true时,“a”将创建一个新的NameState,然后“b”和“c”将重用这个相同的NameState。这是通过存储我们已经拥有键子序列“key1”的 NameState 来完成的。
请注意,每个 addRule 使用不同的规则名称或相同的规则名称并不重要。
此方法的所有形式都具有相同的第一个参数,即提供规则名称并由rulesForEvent()
返回的字符串。其余参数提供名称/值对。它们可以像上面的示例一样以 JSON 形式提供(通过 String、Reader、InputStream 或byte[]
),或者作为Map
提供,其中键是字段名称和值是可能匹配的列表;使用上面的示例,将有一个名为detail.state
的键,其值将是包含"initializing"
和"running"
的列表。
注意:此方法(以及deleteRule()
)是同步的,因此在任何时间点都可能只有一个线程更新机器。
您可以使用相同的名称但多个不同的名称/值模式多次调用addRule()
,从而实现“或”关系;如果任何模式匹配, rulesForEvent()
将返回该名称。
例如,假设您调用规则名称为“R1”的addRule()
并添加以下模式:
{
"detail" : {
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ]
}
}
然后使用相同的名称但不同的模式再次调用它:
{
"detail" : {
"x-limit" : [ { "numeric" : [ "=" , 3.018e2 ] } ]
}
}
此后,对于c-count
数值 2或x-limit
值 301.8, rulesForEvent()
将返回“R1”。
这是addRule()
的镜像;在每种情况下,第一个参数都是规则名称,以字符串形式给出。后续参数提供名称和值,并且可以通过与addRule()
相同的任何方式给出。
注意:此方法(以及addRule()
)是同步的,因此在任何时间点都可能只有一个线程更新机器。
该 API 的操作可能很微妙。机器将名称/值模式到规则名称的映射编译成有限自动机,但不记得哪些模式映射到给定的规则名称。因此,不要求deleteRule()
中的模式与相应的addRule()
中的模式完全匹配。 Ruler 将查找名称/值模式的匹配项,并查看它们是否与具有所提供名称的规则匹配,如果是,则删除它们。请记住,虽然执行与相应的 addRule( deleteRule()
addRule()
调用不会失败,并且不会使机器处于不一致的状态,但它们可能会导致机器中堆积“垃圾”。
一个具体的结果是,如果您使用相同的名称但不同的模式多次调用addRule()
,如上面的规则和规则名称部分所示,则必须使用相同的名称调用deleteRule()
相同的次数。关联模式,以从计算机中删除对该规则名称的所有引用。
此方法返回 Machine 的List
(对于 GenericMachine 返回List
),其中包含与提供的事件匹配的规则的名称。事件可以作为表示其 JSON 形式的单个String
提供给任一方法。
该事件还可以作为交替字段名称和值的字符串集合提供给rulesForEvent()
,并且必须按字段名称按词法排序。这可能是List
或String[]
。
以 JSON 形式提供事件是推荐的方法,并且具有多个优点。首先,按名称排序的顺序用交替的名称/值数量填充字符串列表或数组是很棘手的,并且标尺没有帮助,如果列表结构不正确,则无法正常工作。更困难的是,以字符串形式提供的字段值的表示必须遵循 JSON 语法规则 - 请参阅下面的JSON 文本匹配。
最后,事件的列表/数组版本使得 Ruler 无法识别数组结构并提供数组一致的匹配,如本文档下面所述。不推荐使用rulesForEvent(String eventJSON)
API,而是使用rulesForJSONEvent()
特别是因为它不支持数组一致匹配。
rulesForJSONEvent()
另一个优点是,将事件的 JSON 形式转换为排序列表的代码已经过广泛的分析和优化。
rulesForEvent()
和rulesForJSONEvent()
的性能不依赖于addRule()
添加的规则数量。由于优化了事件处理, rulesForJSONEvent()
通常更快。如果您进行自己的事件处理并使用预先排序的名称和值列表调用rulesForEvent()
,那么速度会更快;但是您可能无法像rulesForJSONEvent()
那样快地准备字段列表。
此方法粗略地计算了机器内的物体数量。它的值仅随着规则的添加或删除而变化。这对于识别可能需要大量内存的大型机器很有用。由于此方法取决于内部对象的数量,因此当标尺库内部发生更改时,此计数可能会发生变化。该方法在运行时执行所有计算,以避免占用内存并使大型规则机的影响变得更糟。它的计算故意不是线程安全的,以避免阻塞规则评估和机器更改。这意味着,如果并行进程正在向计算机添加或删除,则与此类并行进程完成时相比,您可能会得到不同的结果。此外,由于该库针对某些模式对其内部进行了优化(有关更多详细信息,请参阅ShortcutTransition.java
),根据添加或删除规则的顺序,您也可能会得到不同的结果。
如果您将事件视为名称/值对而不是嵌套的 JSON 样式文档,则Patterns
类(及其Range
子类)在构建规则时可能很有用。以下静态方法很有用。
public static ValuePatterns exactMatch ( final String value );
public static ValuePatterns prefixMatch ( final String prefix );
public static ValuePatterns prefixEqualsIgnoreCaseMatch ( final String prefix );
public static ValuePatterns suffixMatch ( final String suffix );
public static ValuePatterns suffixEqualsIgnoreCaseMatch ( final String suffix );
public static ValuePatterns equalsIgnoreCaseMatch ( final String value );
public static ValuePatterns wildcardMatch ( final String value );
public static AnythingBut anythingButMatch ( final String anythingBut );
public static AnythingBut anythingButMatch ( final Set < String > anythingButs );
public static AnythingBut anythingButMatch ( final double anythingBut );
public static AnythingBut anythingButNumberMatch ( final Set < Double > anythingButs );
public static AnythingButValuesSet anythingButPrefix ( final String prefix );
public static AnythingButValuesSet anythingButPrefix ( final Set < String > anythingButs );
public static AnythingButValuesSet anythingButSuffix ( final String suffix );
public static AnythingButValuesSet anythingButSuffix ( final Set < String > anythingButs );
public static AnythingButValuesSet anythingButIgnoreCaseMatch ( final String anythingBut );
public static AnythingButValuesSet anythingButIgnoreCaseMatch ( final Set < String > anythingButs );
public static AnythingButValuesSet anythingButWildcard ( final String value );
public static AnythingButValuesSet anythingButWildcard ( final Set < String > anythingButs );
public static ValuePatterns numericEquals ( final double val );
public static Range lessThan ( final double val );
public static Range lessThanOrEqualTo ( final double val );
public static Range greaterThan ( final double val );
public static Range greaterThanOrEqualTo ( final double val );
public static Range between ( final double bottom , final boolean openBottom , final double top , final boolean openTop );
一旦您使用这些方法构建了适当的Patterns
匹配器,您就可以使用以下方法在您的计算机中添加或删除:
public void addPatternRule ( final String name , final Map < String , List < Patterns >> namevals );
public void deletePatternRule ( final String name , final Map < String , List < Patterns >> namevals );
注意: deleteRule()
中列出的注意事项也适用于deletePatternRule()
。
规则中的字段值必须以其 JSON 表示形式提供。也就是说,字符串值必须用“引号”括起来。允许不带引号的值,例如数字 ( -3.0e5
) 和某些特定于 JSON 的文字( true
、 false
和null
)。
如果以 JSON 形式向addRule()
() 提供规则,或者如果您使用模式而不是文字字符串,则可以完全忽略这一点。但是,如果您以名称/值对的形式提供规则,并且想要指定字段 "xyz" 与字符串 "true" 匹配,则必须将其表示为"xyz", ""true""
。另一方面, "xyz", "true"
将仅匹配 JSON 文字true
。
Ruler 支持对包含数组的事件进行规则匹配,但仅当事件以 JSON 形式提供时 - 当它是预排序字段的列表时,事件中的数组结构会丢失。该行为还取决于您使用的是rulesForEvent()
还是rulesForJSONEvent
。
考虑以下事件。
{
"employees" : [
{ "firstName" : "John" , "lastName" : "Doe" } ,
{ "firstName" : "Anna" , "lastName" : "Smith" } ,
{ "firstName" : "Peter" , "lastName" : "Jones" }
]
}
那么这条规则将匹配:
{ "employees" : { "firstName" : [ "Anna" ] } }
也就是说,数组结构被“淘汰”出规则模式,并且任何包含的对象都被视为父字段的值。这也适用于多级数组:
{
"employees" : [
[
{ "firstName" : "John" , "lastName" : "Doe" } ,
{ "firstName" : "Anna" , "lastName" : "Smith" }
] ,
[
{ "firstName" : "Peter" , "lastName" : "Jones" }
]
]
}
在早期版本的 Ruler 中,唯一基于机器的匹配方法是rulesForEvent()
,不幸的是它也将匹配以下规则:
{ "employees" : { "firstName" : [ "Anna" ] , "lastName" : [ "Jones" ] } }
作为修复,Ruler 引入了rulesForJSONEvent()
,顾名思义,它仅匹配以 JSON 形式提供的事件。 rulesForJsonEvent()
不会匹配上面的“Anna”/“Jones”规则。
正式来说: rulesForJSONEvent()
将拒绝识别任何两个字段位于同一数组的不同元素中的 JSON 对象内的任何匹配。实际上,这意味着它符合您的预期。
有一个支持类com.amazon.fsm.ruler.RuleCompiler
。它包含一个名为check()
的方法,该方法接受 JSON 规则定义并返回一个 String 值,如果为 null,则意味着该规则在语法上有效。如果返回值非 Null,则它包含描述问题的人类可读错误消息。
为了方便起见,它还包含一个名为compile()
方法,它的工作方式与check()
类似,但通过抛出IOException来发出错误信号,并在成功时以Machine的addRule()
的形式返回Map
addRule()
方法期望。由于 Machine 类在内部使用此方法,因此此方法可能会节省时间。
当 Ruler 编译键时,它使用点 ( .
) 作为连接字符。这意味着它将把以下两个规则编译为相同的内部表示
## has no dots in keys
{ "detail" : { "state" : { "status" : [ "running" ] } } }
## has dots in keys
{ "detail" : { "state.status" : [ "running" ] } }
这也意味着这些规则将与以下两个事件匹配:
## has no dots in keys
{ "detail" : { "state" : { "status" : "running" } } }
## has dots in keys
{ "detail" : { "state.status" : "running" } }
此行为可能会在未来版本中发生变化(以避免任何混淆),因此不应依赖此行为。
我们通过将多个规则编译到机器中并匹配以 JSON 字符串形式提供的事件来衡量 Ruler 的性能。
一个基准测试,处理 213,068 个 JSON 事件,平均大小约为 900 字节,每个事件有 5 个完全匹配、前缀匹配、后缀匹配、等于忽略大小写匹配、通配符匹配、数字匹配和非匹配的内容规则并计算匹配数,在 2019 款 MacBook 上产生以下结果:
事件处理速度超过 220K/秒,但以下情况除外:
以下是一些关于处理规则和事件的建议:
从性能考虑,Ruler 对以下几项比较敏感,因此,当您设计事件和规则的架构时,这里有一些建议:
请参阅贡献以获取更多信息。
该项目根据 Apache-2.0 许可证获得许可。请参阅许可证了解更多信息。