Event Ruler (для краткости называемая Ruler в остальной части документа) — это библиотека Java, которая позволяет сопоставлять правила с событиями . Событие — это список полей, которые могут быть заданы как пары имя/значение или как объект JSON. Правило связывает имена полей событий со списками возможных значений. Есть две причины использовать Ruler:
Содержание:
Проще всего объяснить на примере.
Событие — это объект 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 имеет API для предоставления событий как в форме JSON, так и в виде пар имя/значение:
+--------------+------------------------------------------+
| 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" } } ]
}
Сопоставления префикса «равно-игнорировать-регистр» работают только с полями со строковыми значениями.
{
"source" : [ { "suffix" : "ec2" } ]
}
Сопоставления суффиксов работают только с полями со строковыми значениями.
{
"source" : [ { "suffix" : { "equals-ignore-case" : "EC2" } } ]
}
Соответствие суффиксу «равно-игнорировать-регистр» работает только с полями со строковыми значениями.
{
"source" : [ { "equals-ignore-case" : "EC2" } ]
}
Сопоставления «равно-игнорировать-регистр» работают только с полями со строковыми значениями.
{
"source" : [ { "wildcard" : "Simple*Service" } ]
}
Соответствия по подстановочным знакам работают только с полями со строковыми значениями. Одно значение может содержать от нуля до нескольких символов подстановки, но последовательные символы подстановки не допускаются. Чтобы точно соответствовать символу звездочки, подстановочный знак можно экранировать обратной косой чертой. Две последовательные обратные косые черты (т. е. обратная косая черта, экранированная обратной косой чертой) представляют собой фактический символ обратной косой черты. Обратная косая черта, экранирующая любой символ, кроме звездочки или обратной косой черты, не допускается.
Все, кроме сопоставления, делает то, что указано в названии: соответствует всему, кроме того, что указано в правиле.
Что угодно, но работает с одиночными строковыми и числовыми значениями или списками, которые должны содержать полностью строки или полностью числовые значения. Его также можно применять к префиксу, суффиксу или совпадению строки или списка строк с учетом регистра равенства.
Одиночное что угодно, но (строковое, затем числовое):
{
"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. Числовое сопоставление поддерживает ту же точность и диапазон, что и double
примитив Java, который реализует стандарт IEEE binary64
.
{
"detail" : {
"source-ip" : [ { "cidr" : "10.0.0.0/24" } ]
}
}
Это также работает с адресами IPv6.
Сопоставление Exists работает при наличии или отсутствии поля в событии 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 применил логику «И». для всех полей по умолчанию без примитива «И» .
Есть два способа достичь эффекта «ИЛИ»:
Примитив «$или», позволяющий клиенту напрямую описать связь «или» между полями в правиле.
Линейка распознает отношение «ИЛИ» только в том случае, если правило удовлетворяет всем нижеперечисленным условиям:
/src/main/software/amazon/event/ruler/Constants.java#L38
например, приведенное ниже правило не будет анализироваться как « Или» связь, потому что «числовой» и «префикс» являются ключевыми словами, зарезервированными линейкой. {
"$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 изо всех сил старается поддерживать обратную совместимость. Только при соблюдении трех условий, упомянутых выше, правитель изменит свое поведение, поскольку он предполагает, что ваше правило действительно требовало ИЛИ и до сегодняшнего дня было неправильно настроено. Например, приведенное ниже правило будет продолжать работать как обычное правило, рассматривая «$or» как обычное имя поля в правиле и событии:
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : {
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
}
}
Дополнительные примеры того, как «$or» анализируется Ruler как обычное имя поля, см. в файле /src/test/data/normalRulesWithOrWording.json
Ключевое слово «$или» в качестве примитива связи «Или» не должно быть спроектировано как обычное поле как в Событиях, так и в Правилах. Ruler поддерживает устаревшие правила, в которых «$or» анализируется как обычное имя поля, чтобы сохранить обратную совместимость и дать команде время перенести устаревшее использование «$or» из своих событий и правил в качестве обычного имени файла. Совместное использование «$or» в качестве примитива «Or» и «$or» в качестве обычного имени поля не поддерживается Ruler намеренно, чтобы избежать возникновения неудобных двусмысленностей в «$or».
Есть два способа использования линейки. Вы можете скомпилировать несколько правил в «Машину», а затем использовать либо ее метод rulesForEvent()
, либо методы rulesForJSONEvent()
чтобы проверить, какое из правил соответствует какому-либо событию. Разница между этими двумя методами обсуждается ниже. В этом обсуждении rulesForEvent()
будут использоваться в общем, за исключением тех случаев, когда разница имеет значение.
Альтернативно вы можете использовать один статический логический метод, чтобы определить, соответствует ли отдельное событие определенному правилу.
Существует единственный статический логический метод Ruler.matchesRule(event, rule)
— оба аргумента предоставляются в виде строк JSON.
ПРИМЕЧАНИЕ. Существует еще один устаревший метод Ruler.matches(event, rule)
, который не следует использовать, поскольку его результаты несовместимы с rulesForJSONEvent()
и rulesForEvent()
. Подробности смотрите в документации Ruler.matches(event, rule)
.
Время сопоставления не зависит от количества правил. Это лучший выбор, если у вас есть несколько возможных правил, из которых вы хотите выбрать, и особенно если у вас есть способ сохранить скомпилированную машину.
На время сопоставления влияет степень недетерминированности, вызванная правилами подстановочных знаков и правилами, отличными от подстановочных знаков. Производительность ухудшается, поскольку все большее число префиксов правил с подстановочными знаками соответствует теоретическому наихудшему событию. Чтобы избежать этого, в правилах подстановочных знаков, относящихся к одному и тому же полю события, следует избегать общих префиксов, ведущих к их первому подстановочному знаку. Если требуется общий префикс, используйте минимальное количество подстановочных знаков и ограничьте повторяющиеся последовательности символов, которые следуют за подстановочным знаком. 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 была создана первой, а строки, которые она сохраняла и возвращала, считались именами правил.
В целях безопасности тип, используемый для «именования» правил, должен быть неизменяемым. Если вы измените содержимое объекта, пока он используется в качестве имени правила, это может нарушить работу Ruler.
Конструкторы GenericMachine и Machine дополнительно принимают объект GenericMachineConfiguration, который предоставляет следующие параметры конфигурации.
По умолчанию: false Обычно NameStates повторно используются для данной ключевой подпоследовательности и шаблона, если эта ключевая подпоследовательность и шаблон были добавлены ранее или если шаблон уже был добавлен для данной ключевой подпоследовательности. Следовательно, по умолчанию повторное использование NameState является оппортунистическим. Но если для этого флага установлено значение true, повторное использование NameState будет принудительно для ключевой подпоследовательности. Это означает, что первый шаблон, добавляемый для ключевой подпоследовательности, будет повторно использовать NameState, если эта ключевая подпоследовательность была добавлена ранее. Это означает, что каждая ключевая подпоследовательность имеет одно состояние NameState. В некоторых случаях это экспоненциально улучшает использование памяти, но приводит к тому, что в отдельных NameStates сохраняется больше подправил, которые Ruler иногда перебирает, что может вызвать умеренное снижение производительности во время выполнения. По умолчанию это значение равно false для обратной совместимости, но, вероятно, все приложения, кроме наиболее чувствительных к задержкам, выиграют от установки этого значения в значение true.
Вот простой пример. Учитывать:
machine . addRule ( "0" , "{"key1": ["a", "b", "c"]}" ) ;
Шаблон «a» создает NameState, а затем, даже при дополнительном NameStateReuse=false, второй шаблон («b») и третий шаблон («c») повторно используют тот же самый NameState. Но вместо этого рассмотрим следующее:
machine . addRule ( "0" , "{"key1": ["a"]}" ) ;
machine . addRule ( "1" , "{"key1": ["b"]}" ) ;
machine . addRule ( "2" , "{"key1": ["c"]}" ) ;
Теперь, при дополнительном NameStateReuse=false, мы получаем три NameState, поскольку первый шаблон, встречающийся для ключевой подпоследовательности при каждом добавлении правила, создаст новый NameState. Итак, «a», «b» и «c» получают свои собственные состояния имен. Однако при дополнительном NameStateReuse=true «a» создаст новый NameState, затем «b» и «c» будут повторно использовать этот же NameState. Это достигается за счет сохранения того, что у нас уже есть NameState для ключевой подпоследовательности «key1».
Обратите внимание, что не имеет значения, использует ли каждый addRule другое имя правила или одно и то же имя правила.
Все формы этого метода имеют один и тот же первый аргумент — строку, которая предоставляет имя правила и возвращается rulesForEvent()
. Остальные аргументы предоставляют пары имя/значение. Они могут быть предоставлены в формате JSON, как в примерах выше (через String, Reader, InputStream или byte[]
) или в виде Map
, где ключами являются имена полей и значения — это список возможных совпадений; в приведенном выше примере будет ключ с именем detail.state
, значение которого будет списком, содержащим "initializing"
и "running"
.
Примечание. Этот метод (а также deleteRule()
) синхронизирован, поэтому в любой момент времени только один поток может обновлять машину.
Вы можете вызывать addRule()
несколько раз с одним и тем же именем, но с несколькими разными шаблонами имени/значения, таким образом достигая отношения «или»; rulesForEvent()
вернет это имя, если какой-либо из шаблонов соответствует.
Например, предположим, что вы вызываете addRule()
с именем правила «R1» и добавляете следующий шаблон:
{
"detail" : {
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ]
}
}
Затем вы вызываете его снова с тем же именем, но по другому шаблону:
{
"detail" : {
"x-limit" : [ { "numeric" : [ "=" , 3.018e2 ] } ]
}
}
После этого rulesForEvent()
вернет «R1» либо для значения c-count
равного 2 , либо для значения x-limit
, равного 301,8.
Это зеркальное отображение addRule()
; в каждом случае первым аргументом является имя правила, заданное в виде строки. Последующие аргументы предоставляют имена и значения и могут быть заданы любым из тех же способов, что и при использовании addRule()
.
Примечание. Этот метод (а также addRule()
) синхронизирован, поэтому в любой момент времени только один поток может обновлять машину.
Работа этого API может быть незаметной. Машина компилирует сопоставление шаблонов имени/значения с именами правил в конечном автомате, но не запоминает, какие шаблоны сопоставлены с данным именем правила. Таким образом, нет требования, чтобы шаблон в deleteRule()
точно соответствовал шаблону в соответствующем addRule()
. Ruler будет искать совпадения с шаблонами имени/значения и проверять, соответствуют ли они правилу с указанным именем, и если да, то удалять их. Имейте в виду, что при выполнении вызовов deleteRule()
, которые не совсем соответствуют соответствующим вызовам addRule()
не возникнет сбой и не останется машина в несогласованном состоянии, они могут привести к накоплению «мусора» на машине.
Конкретным последствием является то, что если вы вызвали addRule()
несколько раз с одним и тем же именем, но с разными шаблонами, как показано выше в разделе «Правила и имена правил» , вам придется вызывать deleteRule()
такое же количество раз с одинаковыми связанные шаблоны, чтобы удалить все ссылки на это имя правила с компьютера.
Этот метод возвращает List
для Machine (и List
для GenericMachine), который содержит имена правил, соответствующих предоставленному событию. Событие может быть предоставлено любому методу в виде одной String
представляющей его форму JSON.
Событие также может быть передано в rulesForEvent()
как набор строк, которые чередуют имена и значения полей и должны быть лексически отсортированы по имени поля. Это может быть List
или String[]
.
Предоставление события в формате JSON является рекомендуемым подходом и имеет ряд преимуществ. Прежде всего, заполнение списка или массива строк переменными величинами имени/значения в порядке, отсортированном по имени, сложно, и Ruler не помогает, а просто не работает правильно, если список неправильно структурирован. Еще больше усложняет то, что представление значений полей в виде строк должно соответствовать правилам синтаксиса JSON — см. ниже в разделе «Сопоставление текста JSON» .
Наконец, версия события в виде списка/массива делает невозможным для Ruler распознавание структур массива и обеспечение согласованного с массивом сопоставления, как описано ниже в этом документе. API rulesForEvent(String eventJSON)
устарел в пользу 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
).
Это можно полностью игнорировать, если правила предоставляются addRule()
() в форме JSON или если вы работаете с шаблонами, а не с литеральными строками. Но если вы предоставляете правила в виде пар имя/значение и хотите указать, что поле «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()
не будет соответствовать приведенному выше правилу «Анна»/«Джонс».
Формально: rulesForJSONEvent()
откажется распознавать любые совпадения, в которых любые два поля находятся внутри объектов JSON, находящихся в разных элементах одного и того же массива. На практике это означает, что он делает то, что вы ожидаете.
Существует поддерживающий класс com.amazon.fsm.ruler.RuleCompiler
. Он содержит метод с именем check()
который принимает определение правила JSON и возвращает строковое значение, которое, если оно равно нулю, означает, что правило синтаксически допустимо. Если возвращаемое значение не равно NULL, оно содержит удобочитаемое сообщение об ошибке, описывающее проблему.
Для удобства он также содержит метод с именем compile()
который работает так же, как и check()
, но сигнализирует об ошибке, вызывая исключение IOException, и в случае успеха возвращает 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" } }
Это поведение может измениться в будущих версиях (во избежание путаницы), и на него не следует полагаться.
Мы измеряем производительность Ruler путем компиляции нескольких правил в машину и сопоставления событий, представленных в виде строк JSON.
Тест, который обрабатывает 213 068 событий JSON со средним размером около 900 байт против 5 каждого: точное совпадение, совпадение префикса, совпадение суффикса, совпадение с регистром, совпадение с подстановочными знаками, числовое совпадение и все, кроме совпадения. правила и подсчитывает совпадения, на MacBook 2019 года выдает следующее:
События обрабатываются со скоростью более 220 тыс./сек, за исключением:
Вот несколько предложений по правилам обработки и событиям:
С точки зрения производительности, Ruler чувствителен к следующим элементам, поэтому при разработке схемы вашего события и правила, вот несколько советов:
Для получения дополнительной информации см. ВКЛАД.
Этот проект распространяется по лицензии Apache-2.0. См. ЛИЦЕНЗИЮ для получения дополнительной информации.