Event Ruler (llamado Ruler en el resto del documento por brevedad) es una biblioteca de Java que permite hacer coincidir reglas con eventos . Un evento es una lista de campos, que se pueden proporcionar como pares de nombre/valor o como un objeto JSON. Una regla asocia nombres de campos de eventos con listas de valores posibles. Hay dos razones para utilizar Ruler:
Contenido:
Es más fácil de explicar con el ejemplo.
Un evento es un objeto JSON. He aquí un ejemplo:
{
"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"
}
}
También puede ver esto como un conjunto de pares de nombre/valor. Por razones de brevedad, presentamos sólo una muestra. Ruler tiene API para proporcionar eventos tanto en formato JSON como en pares de nombre/valor:
+--------------+------------------------------------------+
| name | value |
|--------------|------------------------------------------|
| source | "aws.ec2" |
| detail-type | "EC2 Instance State-change Notification" |
| detail.state | "running" |
+--------------+------------------------------------------+
Los eventos en formato JSON se pueden proporcionar en forma de una cadena JSON sin formato o un Jackson JsonNode analizado.
Todas las reglas de esta sección coinciden con el evento de ejemplo anterior:
{
"detail-type" : [ "EC2 Instance State-change Notification" ] ,
"resources" : [ "arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000" ] ,
"detail" : {
"state" : [ "initializing" , "running" ]
}
}
Esto hará coincidir cualquier evento con los valores proporcionados para los valores de resource
, detail-type
y detail.state
, ignorando cualquier otro campo en el evento. También coincidiría si el valor de detail.state
hubiera sido "initializing"
.
Los valores de las reglas siempre se proporcionan como matrices y coinciden si el valor del evento es uno de los valores proporcionados en la matriz. La referencia a resources
muestra que si el valor del evento también es una matriz, la regla coincide si la intersección entre la matriz del evento y la matriz de reglas no está vacía.
{
"time" : [ { "prefix" : "2017-10-02" } ]
}
Las coincidencias de prefijo solo funcionan en campos con valores de cadena.
{
"source" : [ { "prefix" : { "equals-ignore-case" : "EC2" } } ]
}
Las coincidencias de prefijo igual a ignorar mayúsculas y minúsculas solo funcionan en campos con valores de cadena.
{
"source" : [ { "suffix" : "ec2" } ]
}
Las coincidencias de sufijos solo funcionan en campos con valores de cadena.
{
"source" : [ { "suffix" : { "equals-ignore-case" : "EC2" } } ]
}
Las coincidencias de sufijo igual a ignorar mayúsculas y minúsculas solo funcionan en campos con valores de cadena.
{
"source" : [ { "equals-ignore-case" : "EC2" } ]
}
Las coincidencias de igual a ignorar mayúsculas y minúsculas solo funcionan en campos con valores de cadena.
{
"source" : [ { "wildcard" : "Simple*Service" } ]
}
Las coincidencias con comodines solo funcionan en campos con valores de cadena. Un único valor puede contener de cero a muchos caracteres comodín, pero no se permiten caracteres comodín consecutivos. Para que coincida específicamente con el carácter de asterisco, se puede utilizar una barra invertida como carácter de escape para un carácter comodín. Dos barras invertidas consecutivas (es decir, una barra invertida con una barra invertida) representan el carácter de barra invertida real. No se permite una barra invertida que escape a cualquier carácter que no sea un asterisco o una barra invertida.
Cualquier cosa menos coincidencia hace lo que dice el nombre: coincide con cualquier cosa excepto lo proporcionado en la regla.
Anything-but funciona con listas o valores numéricos y de cadena única, que deben contener cadenas enteras o números enteramente numéricos. También se puede aplicar a un prefijo, sufijo o coincidencia de mayúsculas y minúsculas de una cadena o una lista de cadenas.
Cualquier cosa menos (cadena, luego numérica):
{
"detail" : {
"state" : [ { "anything-but" : "initializing" } ]
}
}
{
"detail" : {
"x-limit" : [ { "anything-but" : 123 } ]
}
}
Todo menos lista (cadenas):
{
"detail" : {
"state" : [ { "anything-but" : [ "stopped" , "overloaded" ] } ]
}
}
Todo menos lista (números):
{
"detail" : {
"x-limit" : [ { "anything-but" : [ 100 , 200 , 300 ] } ]
}
}
Todo menos prefijo:
{
"detail" : {
"state" : [ { "anything-but" : { "prefix" : "init" } } ]
}
}
Lista de todo menos prefijos (cadenas):
{
"detail" : {
"state" : [ { "anything-but" : { "prefix" : [ "init" , "error" ] } } ]
}
}
Todo menos sufijo:
{
"detail" : {
"instance-id" : [ { "anything-but" : { "suffix" : "1234" } } ]
}
}
Lista de todo menos sufijos (cadenas):
{
"detail" : {
"instance-id" : [ { "anything-but" : { "suffix" : [ "1234" , "6789" ] } } ]
}
}
Caso-todo-menos-ignorar:
{
"detail" : {
"state" : [ { "anything-but" : { "equals-ignore-case" : "Stopped" } } ]
}
}
Lista de cualquier caso menos ignorar (cadenas):
{
"detail" : {
"state" : [ { "anything-but" : { "equals-ignore-case" : [ "Stopped" , "OverLoaded" ] } } ]
}
}
Todo menos comodín:
{
"detail" : {
"state" : [ { "anything-but" : { "wildcard" : "*/bin/*.jar" } } ]
}
}
Lista de todo menos comodines (cadenas):
{
"detail" : {
"state" : [ { "anything-but" : { "wildcard" : [ "*/bin/*.jar" , "*/bin/*.class" ] } } ]
}
}
{
"detail" : {
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ] ,
"d-count" : [ { "numeric" : [ "<" , 10 ] } ] ,
"x-limit" : [ { "numeric" : [ "=" , 3.018e2 ] } ]
}
}
Arriba, las referencias a c-count
, d-count
y x-limit
ilustran la coincidencia numérica y solo funcionan con valores que son números JSON. La coincidencia numérica admite la misma precisión y rango que la primitiva double
de Java que implementa el estándar IEEE 754 binary64
.
{
"detail" : {
"source-ip" : [ { "cidr" : "10.0.0.0/24" } ]
}
}
Esto también funciona con direcciones IPv6.
Existe coincidencia de trabajos sobre la presencia o ausencia de un campo en el evento JSON.
La siguiente regla coincidirá con cualquier evento que tenga presente un campo Detail.c-count.
{
"detail" : {
"c-count" : [ { "exists" : true } ]
}
}
La siguiente regla coincidirá con cualquier evento que no tenga el campo detalle.c-count.
{
"detail" : {
"c-count" : [ { "exists" : false } ]
}
}
Nota La coincidencia Exists
solo funciona en los nodos hoja. No funciona en nodos intermedios.
Como ejemplo, el ejemplo anterior exists : false
coincidiría con el siguiente evento:
{
"detail-type" : [ "EC2 Instance State-change Notification" ] ,
"resources" : [ "arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000" ] ,
"detail" : {
"state" : [ "initializing" , "running" ]
}
}
pero también coincidiría con el evento siguiente porque c-count
no es un nodo hoja:
{
"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" } ]
}
}
Como muestran los ejemplos anteriores, Ruler considera que una regla coincide si todos los campos nombrados en la regla coinciden, y considera que un campo coincide si alguno de los valores de campo proporcionados coincide, es decir, Ruler ha aplicado la lógica "Y". a todos los campos de forma predeterminada sin que se requiera la primitiva "Y" .
Hay dos formas de alcanzar los efectos "O":
La primitiva "$o" para permitir al cliente describir directamente la relación "O" entre los campos de la regla.
La regla reconoce la relación "O" solo cuando la regla cumple todas las condiciones siguientes:
/src/main/software/amazon/event/ruler/Constants.java#L38
, por ejemplo, la siguiente regla no se analizará como " O" relación porque "numérico" y "prefijo" son palabras clave reservadas por la regla. {
"$or": [ {"numeric" : 123}, {"prefix": "abc"} ]
}
De lo contrario, Ruler simplemente trata el "$o" como un nombre de archivo normal, al igual que otras cadenas de la regla.
Normal "O":
// Effect of "source" && ("metricName" || "namespace")
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{ "namespace" : [ "AWS/EC2" , "AWS/ES" ] }
]
}
Paralelo "O":
// 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" ] }
]
}
}
"O" tiene una "Y" dentro
// Effect of ("source" && ("metricName" || ("metricType && "namespace") || "scope"))
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
} ,
{ "scope" : [ "Service" ] }
]
}
"O" y "Y" anidados
// 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" ] }
]
}
Es posible que "$or" ya se utilice como clave normal en algunas aplicaciones (aunque probablemente sea poco común). Para estos casos, Ruler hace todo lo posible para mantener la compatibilidad con versiones anteriores. Solo cuando se cumplan las 3 condiciones mencionadas anteriormente, la regla cambiará el comportamiento porque supone que su regla realmente quería un OR y estaba mal configurada hasta hoy. Por ejemplo, la siguiente regla seguirá funcionando como regla normal y tratará "$o" como nombre de campo normal en la regla y el evento:
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : {
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
}
}
Consulte /src/test/data/normalRulesWithOrWording.json
para ver más ejemplos en los que Ruler analiza "$or" como un nombre de campo normal.
La palabra clave "$o" como primitiva de relación "O" no debe diseñarse como un campo normal tanto en Eventos como en Reglas. Ruler admite las reglas heredadas donde "$or" se analiza como nombre de campo normal para mantener la compatibilidad con versiones anteriores y darle tiempo al equipo para migrar su uso heredado de "$or" fuera de sus eventos y reglas como nombre de archivo normal. Ruler no admite intencionalmente el uso combinado de "$or" como primitivo "O" y "$o" como nombre de campo normal para evitar que se produzcan ambigüedades muy incómodas sobre "$o".
Hay dos formas de utilizar Ruler. Puede compilar varias reglas en una "Máquina" y luego usar cualquiera de sus métodos rulesForEvent()
o rulesForJSONEvent()
para verificar cuál de las reglas coincide con cualquier Evento. La diferencia entre estos dos métodos se analiza a continuación. Esta discusión usará rulesForEvent()
genéricamente excepto cuando la diferencia sea importante.
Alternativamente, puede utilizar un único método booleano estático para determinar si un evento individual coincide con una regla particular.
Hay un único método booleano estático Ruler.matchesRule(event, rule)
; ambos argumentos se proporcionan como cadenas JSON.
NOTA: Existe otro método obsoleto llamado Ruler.matches(event, rule)
que no debe usarse ya que sus resultados son inconsistentes con rulesForJSONEvent()
y rulesForEvent()
. Consulte la documentación en Ruler.matches(event, rule)
para obtener más detalles.
El tiempo de coincidencia no depende de la cantidad de reglas. Esta es la mejor opción si tiene varias reglas posibles entre las que desea seleccionar y, especialmente, si tiene una manera de almacenar la máquina compilada.
El tiempo de coincidencia se ve afectado por el grado de no determinismo causado por las reglas de comodines y todo menos comodines. El rendimiento se deteriora a medida que un número cada vez mayor de prefijos de reglas comodín coinciden con el peor de los casos teóricos. Para evitar esto, las reglas de comodines pertenecientes al mismo campo de evento deben evitar prefijos comunes que conduzcan al primer carácter comodín. Si se requiere un prefijo común, utilice la cantidad mínima de caracteres comodín y limite las secuencias de caracteres repetidas que ocurren después de un carácter comodín. MachineComplexityEvaluator se puede utilizar para evaluar una máquina y determinar el grado de no determinismo o "complejidad" (es decir, cuántos prefijos de reglas comodín coinciden con un evento teórico del peor de los casos). A continuación se muestran algunos puntos de datos que muestran una disminución típica en el rendimiento al aumentar las puntuaciones de complejidad.
Es importante limitar la complejidad de la máquina para proteger su aplicación. Existen al menos dos estrategias diferentes para limitar la complejidad de la máquina. Cuál tiene más sentido puede depender de su aplicación.
La estrategia n.° 1 es más ideal porque mide la complejidad real de la máquina que contiene todas las reglas. Cuando sea posible, se debe utilizar esta estrategia. La desventaja es que, digamos, tiene un plano de control que permite la creación de una regla a la vez, hasta un número muy grande. Luego, para cada una de estas operaciones del plano de control, debes cargar todas las reglas existentes para realizar la validación. Esto podría resultar muy caro. También es propenso a las condiciones de carrera. La estrategia número 2 es un compromiso. El umbral utilizado por la estrategia n.º 2 será inferior al de la estrategia n.º 1, ya que es un umbral por regla. Digamos que desea que la complejidad de una máquina, con todas las reglas agregadas, no sea superior a 300. Luego, con la estrategia n.° 2, por ejemplo, podría limitar cada máquina de una sola regla a una complejidad de 10 y permitir 30 reglas que contengan patrones comodín. . En el peor de los casos, donde la complejidad es perfectamente aditiva (poco probable), esto llevaría a una máquina con una complejidad de 300. La desventaja es que es poco probable que la complejidad sea perfectamente aditiva, por lo que el número de reglas que contienen comodines será probablemente se limite innecesariamente.
Para la estrategia n.º 2, dependiendo de cómo se almacenen las reglas, es posible que sea necesario agregar un atributo adicional a las reglas para indicar cuáles son no deterministas (es decir, contienen patrones de comodines) para limitar el número de reglas que contienen comodines.
El siguiente es un fragmento de código que ilustra cómo limitar la complejidad de un patrón determinado, como para la estrategia n.° 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 ; } }
La clase principal con la que interactuará implementa la coincidencia de reglas basada en la máquina de estados. Los métodos interesantes son:
addRule()
: agrega una nueva regla a la máquinadeleteRule()
: elimina una regla de la máquinarulesForEvent()
/ rulesForJSONEvent()
- busca las reglas en la máquina que coinciden con un evento Hay dos tipos: Machine
y GenericMachine
. La máquina es simplemente GenericMachine
. La API se refiere al tipo genérico como "nombre", que refleja la historia: la versión String se creó primero y las cadenas que almacenó y devolvió se consideraron nombres de reglas.
Por seguridad, el tipo utilizado para "nombrar" las reglas debe ser inmutable. Si cambia el contenido de un objeto mientras se utiliza como nombre de regla, esto puede interrumpir el funcionamiento de Ruler.
Los constructores GenericMachine y Machine aceptan opcionalmente un objeto GenericMachineConfiguration, que expone las siguientes opciones de configuración.
Valor predeterminado: falso Normalmente, los NameStates se reutilizan para una subsecuencia y patrón de clave determinados si esta subsecuencia y patrón de clave se han agregado previamente, o si ya se ha agregado un patrón para la subsecuencia de clave determinada. Por lo tanto, de forma predeterminada, la reutilización de NameState es oportunista. Pero al establecer este indicador en verdadero, se forzará la reutilización de NameState para una subsecuencia clave. Esto significa que el primer patrón que se agrega para una subsecuencia clave reutilizará un NameState si esa subsecuencia clave se agregó antes. Lo que significa que cada subsecuencia clave tiene un único NameState. Esto mejora exponencialmente la utilización de la memoria en algunos casos, pero conduce a que se almacenen más subreglas en NameStates individuales, sobre los cuales Ruler a veces itera, lo que puede provocar una modesta regresión del rendimiento en tiempo de ejecución. El valor predeterminado es falso para compatibilidad con versiones anteriores, pero probablemente todas las aplicaciones, excepto las más sensibles a la latencia, se beneficiarían al establecerlo en verdadero.
He aquí un ejemplo sencillo. Considerar:
machine . addRule ( "0" , "{"key1": ["a", "b", "c"]}" ) ;
El patrón "a" crea un NameState y luego, incluso con adicionalNameStateReuse=false, el segundo patrón ("b") y el tercer patrón ("c") reutilizan ese mismo NameState. Pero considere lo siguiente en su lugar:
machine . addRule ( "0" , "{"key1": ["a"]}" ) ;
machine . addRule ( "1" , "{"key1": ["b"]}" ) ;
machine . addRule ( "2" , "{"key1": ["c"]}" ) ;
Ahora, con adicionalNameStateReuse=false, terminamos con tres NameStates, porque el primer patrón encontrado para una subsecuencia clave en cada adición de regla creará un nuevo NameState. Entonces, "a", "b" y "c" obtienen sus propios NameStates. Sin embargo, con adicionalNameStateReuse=true, "a" creará un nuevo NameState, luego "b" y "c" reutilizarán este mismo NameState. Esto se logra almacenando que ya tenemos un NameState para la subsecuencia clave "key1".
Tenga en cuenta que no importa si cada addRule usa un nombre de regla diferente o el mismo nombre de regla.
Todas las formas de este método tienen el mismo primer argumento, una Cadena que proporciona el nombre de la Regla y es devuelta por rulesForEvent()
. El resto de los argumentos proporcionan los pares nombre/valor. Se pueden proporcionar en JSON como en los ejemplos anteriores (a través de String, Reader, InputStream o byte[]
), o como Map
, donde las claves son los nombres de los campos y el los valores son la lista de posibles coincidencias; Usando el ejemplo anterior, habría una clave llamada detail.state
cuyo valor sería la lista que contiene "initializing"
y "running"
.
Nota: Este método (y también deleteRule()
) está sincronizado, por lo que solo un subproceso puede actualizar la máquina en cualquier momento.
Puede llamar addRule()
varias veces con el mismo nombre pero con múltiples patrones de nombre/valor diferentes, logrando así una relación "o"; rulesForEvent()
devolverá ese nombre si alguno de los patrones coincide.
Por ejemplo, suponga que llama addRule()
con el nombre de regla "R1" y agrega el siguiente patrón:
{
"detail" : {
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ]
}
}
Luego lo vuelves a llamar con el mismo nombre pero con un patrón diferente:
{
"detail" : {
"x-limit" : [ { "numeric" : [ "=" , 3.018e2 ] } ]
}
}
Después de esto, rulesForEvent()
devolverá "R1" para un valor c-count
de 2 o un valor x-limit
de 301,8.
Esta es una imagen reflejada de addRule()
; en cada caso, el primer argumento es el nombre de la regla, dado como una cadena. Los argumentos posteriores proporcionan los nombres y valores, y pueden darse de la misma manera que con addRule()
.
Nota: Este método (y también addRule()
) está sincronizado, por lo que solo un subproceso puede actualizar la máquina en cualquier momento.
El funcionamiento de esta API puede ser sutil. La Máquina compila la asignación de patrones de nombre/valor a nombres de Regla en un autómata finito, pero no recuerda qué patrones están asignados a un nombre de Regla determinado. Por lo tanto, no es necesario que el patrón en un deleteRule()
coincida exactamente con el del addRule()
correspondiente. Ruler buscará coincidencias con los patrones de nombre/valor y verá si coinciden con una regla con el nombre proporcionado y, de ser así, las eliminará. Tenga en cuenta que al realizar llamadas deleteRule()
que no coincidan exactamente con las llamadas addRule()
correspondientes no fallarán y no dejarán la máquina en un estado inconsistente, pueden provocar que se acumule "basura" en la máquina.
Una consecuencia específica es que si ha llamado addRule()
varias veces con el mismo nombre pero con diferentes patrones, como se ilustra arriba en la sección Reglas y nombres de reglas , tendría que llamar deleteRule()
la misma cantidad de veces, con el mismo patrones asociados, para eliminar todas las referencias a ese nombre de regla de la máquina.
Este método devuelve una List
para Máquina (y List
para GenericMachine) que contiene los nombres de las reglas que coinciden con el evento proporcionado. El evento se puede proporcionar a cualquiera de los métodos como una única String
que representa su formato JSON.
El evento también se puede proporcionar a rulesForEvent()
como una colección de cadenas que alternan nombres y valores de campo, y deben ordenarse léxicamente por nombre de campo. Puede ser List
o String[]
.
Proporcionar el evento en JSON es el enfoque recomendado y tiene varias ventajas. En primer lugar, completar la lista o matriz de cadenas con cantidades alternas de nombre/valor, en un orden ordenado por nombre, es complicado, y Ruler no ayuda, simplemente no funciona correctamente si la lista está estructurada incorrectamente. Para aumentar la dificultad, la representación de los valores de los campos, proporcionados como cadenas, debe seguir las reglas de sintaxis JSON; consulte a continuación la sección Coincidencia de texto JSON .
Finalmente, la versión de lista/matriz de un evento hace imposible que Ruler reconozca las estructuras de la matriz y proporcione coincidencias consistentes con la matriz, como se describe a continuación en este documento. La API rulesForEvent(String eventJSON)
está obsoleta en favor de rulesForJSONEvent()
específicamente porque no admite coincidencias consistentes con matrices.
rulesForJSONEvent()
también tiene la ventaja de que el código que convierte la forma JSON del evento en una lista ordenada ha sido ampliamente perfilado y optimizado.
El rendimiento de rulesForEvent()
y rulesForJSONEvent()
no depende de la cantidad de reglas agregadas con addRule()
. rulesForJSONEvent()
es generalmente más rápido debido al procesamiento de eventos optimizado. Si realiza su propio procesamiento de eventos y llama rulesForEvent()
con una lista previamente ordenada de nombres y valores, eso es aún más rápido; pero es posible que no pueda preparar la lista de campos tan rápido como lo hace rulesForJSONEvent()
.
Este método calcula aproximadamente el número de objetos dentro de la máquina. Su valor sólo varía según se agregan o eliminan reglas. Esto es útil para identificar máquinas grandes que potencialmente requieren mucha memoria. Como este método depende de la cantidad de objetos internos, estos recuentos pueden cambiar cuando se cambian los elementos internos de la biblioteca de reglas. El método realiza todos sus cálculos en tiempo de ejecución para evitar ocupar memoria y empeorar el impacto de las grandes máquinas de reglas. Su cálculo NO es intencionalmente seguro para subprocesos para evitar el bloqueo de evaluaciones de reglas y cambios de máquina. Significa que si se agrega o elimina un proceso paralelo de la máquina, es posible que obtenga resultados diferentes en comparación con cuando se completan dichos procesos paralelos. Además, como la biblioteca realiza optimizaciones internas para algunos patrones (consulte ShortcutTransition.java
para obtener más detalles), también puede obtener resultados diferentes según el orden en que se agregaron o eliminaron las reglas.
Si piensa en sus eventos como pares de nombre/valor en lugar de documentos de estilo JSON anidados, la clase Patterns
(y su subclase Range
) puede resultar útil para construir reglas. Los siguientes métodos estáticos son útiles.
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 );
Una vez que haya creado comparadores de Patterns
apropiados con estos métodos, puede utilizar los siguientes métodos para agregar o eliminar de su máquina:
public void addPatternRule ( final String name , final Map < String , List < Patterns >> namevals );
public void deletePatternRule ( final String name , final Map < String , List < Patterns >> namevals );
NOTA: Las precauciones enumeradas en deleteRule()
también se aplican a deletePatternRule()
.
Los valores de campo en las reglas se deben proporcionar en sus representaciones JSON. Es decir, los valores de cadena deben estar entre comillas. Se permiten valores sin comillas, como números ( -3.0e5
) y ciertos literales específicos de JSON ( true
, false
y null
).
Esto se puede ignorar por completo si se proporcionan reglas para addRule()
() en formato JSON, o si está trabajando con patrones en lugar de cadenas literales. Pero si proporciona reglas como pares de nombre/valor y desea especificar que el campo "xyz" coincida con la cadena "true", debe expresarse como "xyz", ""true""
. Por otro lado, "xyz", "true"
coincidiría solo con el literal JSON true
.
Ruler admite la coincidencia de reglas para eventos que contienen matrices, pero solo cuando el evento se proporciona en formato JSON; cuando es una lista de campos preordenados, la estructura de la matriz en el evento se pierde. El comportamiento también depende de si usas rulesForEvent()
o rulesForJSONEvent
.
Considere el siguiente evento.
{
"employees" : [
{ "firstName" : "John" , "lastName" : "Doe" } ,
{ "firstName" : "Anna" , "lastName" : "Smith" } ,
{ "firstName" : "Peter" , "lastName" : "Jones" }
]
}
Entonces esta regla coincidirá:
{ "employees" : { "firstName" : [ "Anna" ] } }
Es decir, la estructura de la matriz se "elimina" del patrón de reglas y cualquier objeto contenido se trata como si fuera el valor del campo principal. Esto también funciona para matrices de varios niveles:
{
"employees" : [
[
{ "firstName" : "John" , "lastName" : "Doe" } ,
{ "firstName" : "Anna" , "lastName" : "Smith" }
] ,
[
{ "firstName" : "Peter" , "lastName" : "Jones" }
]
]
}
En versiones anteriores de Ruler, el único método de comparación basado en máquina era rulesForEvent()
que desafortunadamente también coincidirá con la siguiente regla:
{ "employees" : { "firstName" : [ "Anna" ] , "lastName" : [ "Jones" ] } }
Como solución, Ruler introdujo rulesForJSONEvent()
que, como su nombre indica, solo coincide con eventos proporcionados en formato JSON. rulesForJsonEvent()
no coincidirá con la regla "Anna"/"Jones" anterior.
Formalmente: rulesForJSONEvent()
se negará a reconocer cualquier coincidencia en la que dos campos cualesquiera estén dentro de objetos JSON que se encuentren en diferentes elementos de la misma matriz. En la práctica, esto significa que hace lo que cabría esperar.
Existe una clase de soporte com.amazon.fsm.ruler.RuleCompiler
. Contiene un método llamado check()
que acepta una definición de regla JSON y devuelve un valor de cadena que, si es nulo, significa que la regla era sintácticamente válida. Si el valor de retorno no es nulo, contiene un mensaje de error legible por humanos que describe el problema.
Para mayor comodidad, también contiene un método llamado compile()
que funciona igual que check()
pero señala un error lanzando una IOException y, en caso de éxito, devuelve un Map
en la forma en que addRule()
de la máquina addRule()
el método espera. Dado que la clase Machine usa esto internamente, este método puede ahorrarle tiempo.
Cuando Ruler compila claves, utiliza un punto ( .
) como carácter de unión. Esto significa que compilará las dos reglas siguientes en la misma representación interna.
## has no dots in keys
{ "detail" : { "state" : { "status" : [ "running" ] } } }
## has dots in keys
{ "detail" : { "state.status" : [ "running" ] } }
También significa que estas reglas coincidirán con los siguientes dos eventos:
## has no dots in keys
{ "detail" : { "state" : { "status" : "running" } } }
## has dots in keys
{ "detail" : { "state.status" : "running" } }
Este comportamiento puede cambiar en versiones futuras (para evitar confusiones) y no se debe confiar en él.
Medimos el rendimiento de Ruler compilando múltiples reglas en una máquina y haciendo coincidir eventos proporcionados como cadenas JSON.
Un punto de referencia que procesa 213.068 eventos JSON con un tamaño promedio de aproximadamente 900 bytes frente a 5 de cada coincidencia exacta, coincidencia de prefijo, coincidencia de sufijo, coincidencia de mayúsculas y minúsculas iguales, coincidencia de comodines, coincidencia numérica y cualquier cosa menos coincidencia. gobierna y cuenta las coincidencias, produce lo siguiente en una MacBook 2019:
Los eventos se procesan a más de 220 000/segundo, excepto:
A continuación se ofrecen algunas sugerencias sobre el procesamiento de reglas y eventos:
Desde el punto de vista del rendimiento, Ruler es sensible a los siguientes elementos, por lo que, cuando diseñes el esquema de tu evento y regla, aquí tienes algunas sugerencias:
Consulte CONTRIBUCIÓN para obtener más información.
Este proyecto está bajo la licencia Apache-2.0. Consulte LICENCIA para obtener más información.