Event Ruler(나머지 문서에서는 간결하게 Ruler라고 함)는 Rules 를 Events 와 일치시킬 수 있는 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" } } ]
}
접두어 같음-대소문자 무시 일치는 문자열 값 필드에서만 작동합니다.
{
"source" : [ { "suffix" : "ec2" } ]
}
접미사 일치는 문자열 값 필드에서만 작동합니다.
{
"source" : [ { "suffix" : { "equals-ignore-case" : "EC2" } } ]
}
접미사 같음-대소문자 무시 일치는 문자열 값 필드에서만 작동합니다.
{
"source" : [ { "equals-ignore-case" : "EC2" } ]
}
같음-대소문자 무시 일치는 문자열 값 필드에서만 작동합니다.
{
"source" : [ { "wildcard" : "Simple*Service" } ]
}
와일드카드 일치는 문자열 값 필드에서만 작동합니다. 단일 값에는 0개 이상의 와일드카드 문자가 포함될 수 있지만 연속된 와일드카드 문자는 허용되지 않습니다. 별표 문자와 구체적으로 일치시키려면 와일드카드 문자를 백슬래시로 이스케이프할 수 있습니다. 두 개의 연속 백슬래시(즉, 백슬래시로 이스케이프된 백슬래시)는 실제 백슬래시 문자를 나타냅니다. 별표 또는 백슬래시 이외의 문자를 이스케이프하는 백슬래시는 허용되지 않습니다.
일치를 제외한 모든 항목은 이름에서 알 수 있듯이 규칙에 제공된 항목을 제외한 모든 항목과 일치합니다.
무엇이든 가능하지만 전체 문자열 또는 전체 숫자를 포함해야 하는 단일 문자열 및 숫자 값 또는 목록과 함께 작동합니다. 또한 접두사, 접미사 또는 문자열이나 문자열 목록의 대소문자 무시 일치에도 적용될 수 있습니다.
단일 항목(문자열, 숫자 순):
{
"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 숫자 값에만 작동합니다. 숫자 일치는 IEEE 754 binary64
표준을 구현하는 Java의 double
기본 요소와 동일한 정밀도와 범위를 지원합니다.
{
"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" 논리를 적용했습니다. 기본적으로 "And" 프리미티브 없이 모든 필드에 필요합니다 .
"Or" 효과를 얻는 방법에는 두 가지가 있습니다.
고객이 규칙의 필드 간 "Or" 관계를 직접 설명할 수 있도록 하는 "$or" 기본 요소입니다.
눈금자는 규칙이 아래 조건을 모두 충족하는 경우 에만 "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" ] }
]
}
}
"Or" 안에 "And"가 들어있어요
// Effect of ("source" && ("metricName" || ("metricType && "namespace") || "scope"))
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
} ,
{ "scope" : [ "Service" ] }
]
}
중첩된 "Or" 및 "And"
// 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" 사용을 마이그레이션할 수 있는 시간을 제공하는 레거시 규칙을 지원합니다. "$or"를 "Or" 프리미티브로 사용하고 "$or"를 일반 필드 이름으로 혼합 사용하는 것은 "$or"에 대한 매우 어색한 모호성이 발생하는 것을 피하기 위해 Ruler에서 의도적으로 지원하지 않습니다.
눈금자를 사용하는 방법에는 두 가지가 있습니다. 여러 규칙을 "머신"으로 컴파일한 다음 해당 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<T>
의 두 가지 종류가 있습니다. 기계는 단순히 GenericMachine<String>
입니다. API는 내역을 반영하는 "이름"으로 일반 유형을 참조합니다. 문자열 버전이 먼저 구축되었으며 저장 및 반환된 문자열은 규칙 이름으로 간주되었습니다.
안전을 위해 규칙의 "이름 지정"에 사용되는 유형은 변경할 수 없어야 합니다. 규칙 이름으로 사용되는 개체의 내용을 변경하면 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"]}" ) ;
이제 extraNameStateReuse=false를 사용하면 세 개의 NameState가 됩니다. 각 규칙 추가 시 키 하위 시퀀스에 대해 발견된 첫 번째 패턴이 새 NameState를 생성하기 때문입니다. 따라서 "a", "b" 및 "c"는 모두 고유한 NameState를 갖습니다. 그러나 추가적인NameStateReuse=true를 사용하면 "a"는 새 NameState를 생성하고 "b"와 "c"는 이 동일한 NameState를 재사용합니다. 이는 키 하위 시퀀스 "key1"에 대한 NameState가 이미 있음을 저장하여 수행됩니다.
각 addRule이 다른 규칙 이름을 사용하는지, 동일한 규칙 이름을 사용하는지 여부는 중요하지 않습니다.
이 메소드의 모든 형태는 동일한 첫 번째 인수, 즉 규칙의 이름을 제공하고 rulesForEvent()
에 의해 반환되는 문자열을 갖습니다. 나머지 인수는 이름/값 쌍을 제공합니다. 위의 예(String, Reader, InputStream 또는 byte[]
통해)와 같이 JSON으로 제공되거나 Map<String, List<String>>
으로 제공될 수 있습니다. 여기서 키는 필드 이름이고 값은 가능한 일치 목록입니다. 위의 예를 사용하면 "initializing"
및 "running"
포함하는 목록이 값이 되는 detail.state
라는 키가 있습니다.
참고: 이 메서드(및 deleteRule()
)는 동기화되므로 언제든지 하나의 스레드만 시스템을 업데이트할 수 있습니다.