Event Ruler (簡潔にするためにドキュメントの残りの部分では Ruler と呼ばれます) は、 Rules をEventsに照合できるようにする Java ライブラリです。イベントはフィールドのリストであり、名前と値のペアまたは JSON オブジェクトとして指定できます。ルールは、イベント フィールド名を可能な値のリストに関連付けます。 Ruler を使用する理由は 2 つあります。
コンテンツ:
例を挙げて説明するのが最も簡単です。
イベントは 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"
であった場合にも一致します。
ルールの値は常に配列として提供され、イベントの値が配列で提供された値の 1 つである場合に一致します。 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" } ]
}
Equals-ignore-case 一致は文字列値フィールドでのみ機能します。
{
"source" : [ { "wildcard" : "Simple*Service" } ]
}
ワイルドカード一致は文字列値フィールドでのみ機能します。 1 つの値には 0 から多数のワイルドカード文字を含めることができますが、連続したワイルドカード文字は許可されません。特にアスタリスク文字と一致させるには、ワイルドカード文字をバックスラッシュでエスケープできます。 2 つの連続するバックスラッシュ (つまり、バックスラッシュでエスケープされたバックスラッシュ) は、実際のバックスラッシュ文字を表します。アスタリスクまたはバックスラッシュ以外の文字をエスケープするバックスラッシュは許可されません。
Anything but match はその名のとおり、ルールで指定されたもの以外のすべてに一致します。
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 数値の値でのみ機能します。数値マッチングは、IEEE 754 binary64
標準を実装する Java のdouble
プリミティブと同じ精度と範囲をサポートします。
{
"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
match はリーフ ノードでのみ機能します。中間ノードでは機能しません。
例として、 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」効果に到達するには 2 つの方法があります。
「$or」プリミティブにより、顧客はルール内のフィールド間の「または」関係を直接記述することができます。
ルーラーは、ルールが以下のすべての条件を満たしている場合にのみ、「または」関係を認識します。
/src/main/software/amazon/event/ruler/Constants.java#L38
event/ruler/Constants.java#L38 の RESERVED_FIELD_NAMES_IN_OR_RELATIONSHIP を参照してください。以下のルールは「」として解析されません。 「数値」と「プレフィックス」はルーラーの予約キーワードであるため、「OR」関係。 {
"$or": [ {"numeric" : 123}, {"prefix": "abc"} ]
}
それ以外の場合、ルーラーは「$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」の使用をイベントやルールから通常のファイル名として移行する時間を確保できます。 「$or」プリミティブとしての「$or」と通常のフィールド名としての「$or」の混合使用は、「$or」に関する非常に厄介な曖昧さの発生を避けるために、ルーラーでは意図的にサポートされていません。
ルーラーの使用方法は 2 つあります。複数のルールを「マシン」にコンパイルし、そのrulesForEvent()
メソッドまたはrulesForJSONEvent()
メソッドのいずれかを使用して、どのルールがイベントに一致するかを確認できます。これら 2 つの方法の違いについては、以下で説明します。この説明では、違いが重要な場合を除き、一般的にrulesForEvent()
使用します。
あるいは、単一の静的ブール値メソッドを使用して、個々のイベントが特定のルールに一致するかどうかを判断できます。
単一の静的ブール メソッドRuler.matchesRule(event, rule)
があり、両方の引数が JSON 文字列として提供されます。
注: Ruler.matches(event, rule)
と呼ばれる別の非推奨メソッドがあります。これは、結果がrulesForJSONEvent()
およびrulesForEvent()
と矛盾するため、使用しないでください。詳細については、 Ruler.matches(event, rule)
に関するドキュメントを参照してください。
マッチング時間はルールの数には依存しません。これは、選択したいルールが複数ある場合、特にコンパイルされたマシンを保存する方法がある場合に最適です。
マッチング時間は、ワイルドカード ルールおよびワイルドカード以外のルールによって引き起こされる非決定性の程度に影響されます。理論上の最悪のイベントに一致するワイルドカード ルール プレフィックスの数が増えると、パフォーマンスが低下します。これを避けるために、同じイベント フィールドに関連するワイルドカード ルールでは、最初のワイルドカード文字に至るまでの共通のプレフィックスを避ける必要があります。共通の接頭辞が必要な場合は、最小限の数のワイルドカード文字を使用し、ワイルドカード文字の後に出現する文字シーケンスの繰り返しを制限します。 MachineComplexityEvaluator を使用すると、マシンを評価し、非決定性の程度、つまり「複雑さ」(つまり、理論上の最悪のイベントに一致するワイルドカード ルール プレフィックスの数) を判断できます。以下に、複雑さのスコアが増加するとパフォーマンスが低下する一般的なことを示すデータ ポイントをいくつか示します。
アプリケーションを保護するには、マシンの複雑さを制限することが重要です。マシンの複雑さを制限するには、少なくとも 2 つの異なる戦略があります。どちらがより理にかなっているかは、アプリケーションによって異なります。
戦略 #1 は、すべてのルールを含むマシンの実際の複雑さを測定するという点で、より理想的です。可能であれば、この戦略を使用する必要があります。欠点は、一度に 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
の 2 つのフレーバーがあります。 Machine は単にGenericMachine
です。 API はジェネリック型を「名前」として参照します。これは歴史を反映しています。文字列バージョンが最初に構築され、それが保存して返す文字列はルール名と見なされていました。
安全のため、ルールの「名前」に使用される型は不変である必要があります。ルール名として使用されているオブジェクトの内容を変更すると、Ruler の動作が中断される可能性があります。
GenericMachine および Machine コンストラクターは、オプションで、次の構成オプションを公開する GenericMachineConfiguration オブジェクトを受け入れます。
デフォルト: false 通常、指定されたキー サブシーケンスとパターンが以前に追加されている場合、または指定されたキー サブシーケンスにパターンがすでに追加されている場合、NameStates はそのキー サブシーケンスとパターンに対して再利用されます。したがって、デフォルトでは、NameState の再利用は便宜的に行われます。ただし、このフラグを true に設定すると、キーのサブシーケンスに対して NameState の再利用が強制されます。これは、キー サブシーケンスが以前に追加されている場合、キー サブシーケンスに追加される最初のパターンが NameState を再利用することを意味します。つまり、各キー サブシーケンスには単一の NameState があります。これにより、場合によってはメモリ使用率が指数関数的に改善されますが、個々の NameState に保存されるサブルールが増加し、Ruler がそれを反復処理することになり、実行時のパフォーマンスが若干低下する可能性があります。これは、下位互換性のためにデフォルトで false に設定されていますが、最もレイテンシーに敏感なアプリケーションを除くすべてのアプリケーションは、これを true に設定することで恩恵を受ける可能性があります。
以下に簡単な例を示します。考慮する:
machine . addRule ( "0" , "{"key1": ["a", "b", "c"]}" ) ;
パターン「a」は NameState を作成し、additionNameStateReuse=false であっても、2 番目のパターン (「b」) と 3 番目のパターン (「c」) は同じ NameState を再利用します。ただし、代わりに次のことを考慮してください。
machine . addRule ( "0" , "{"key1": ["a"]}" ) ;
machine . addRule ( "1" , "{"key1": ["b"]}" ) ;
machine . addRule ( "2" , "{"key1": ["c"]}" ) ;
ここで、AdditionalNameStateReuse=false を指定すると、最終的に 3 つの NameState が作成されます。これは、ルールを追加するたびにキー サブシーケンスで最初に見つかったパターンによって新しい NameState が作成されるためです。したがって、「a」、「b」、および「c」はすべて独自の NameState を取得します。ただし、AdditionalNameStateReuse=true の場合、「a」は新しい NameState を作成し、「b」と「c」はこの同じ NameState を再利用します。これは、キー サブシーケンス「key1」の NameState が既に存在することを保存することによって実現されます。
各 addRule が異なるルール名を使用するか、同じルール名を使用するかは問題ではないことに注意してください。
このメソッドのすべての形式には同じ最初の引数があり、これはルールの名前を提供し、 rulesForEvent()
によって返される String です。残りの引数は、名前と値のペアを提供します。これらは、上記の例のように (String、Reader、InputStream、またはbyte[]
経由) JSON で提供することも、 Map
として提供することもできます。ここで、キーはフィールド名と値は一致する可能性のあるリストです。上の例を使用すると、 detail.state
という名前のキーがあり、その値は"initializing"
と"running"
を含むリストになります。
注: このメソッド (およびdeleteRule()
) は同期されるため、いつでも 1 つのスレッドだけがマシンを更新できます。
同じ名前で複数の異なる名前/値パターンを使用してaddRule()
複数回呼び出すことができ、これにより「or」関係が得られます。 rulesForEvent()
パターンのいずれかが一致した場合にその名前を返します。
たとえば、ルール名を「R1」としてaddRule()
を呼び出し、次のパターンを追加するとします。
{
"detail" : {
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ]
}
}
次に、同じ名前で異なるパターンで再度呼び出します。
{
"detail" : {
"x-limit" : [ { "numeric" : [ "=" , 3.018e2 ] } ]
}
}
この後、 rulesForEvent()
c-count
値 2またはx-limit
値 301.8 のいずれかに対して「R1」を返します。
これはaddRule()
の鏡像です。いずれの場合も、最初の引数は文字列として指定されるルール名です。後続の引数は名前と値を指定し、 addRule()
と同じ方法で指定できます。
注: このメソッド (およびaddRule()
) は同期されるため、いつでも 1 つのスレッドだけがマシンを更新できます。
この API の操作は微妙な場合があります。マシンは、名前/値パターンのルール名へのマッピングを有限オートマトンにコンパイルしますが、どのパターンが特定のルール名にマッピングされているかを記憶しません。したがって、 deleteRule()
のパターンが対応するaddRule()
のパターンと正確に一致する必要はありません。 Ruler は、名前/値パターンとの一致を検索し、指定された名前のルールと一致するかどうかを確認し、一致する場合はそれらを削除します。対応するaddRule()
呼び出しと完全に一致しないdeleteRule()
呼び出しの実行は失敗せず、マシンが不整合な状態になることはありませんが、マシン内に「ゴミ」が蓄積する可能性があることに注意してください。
具体的な結果として、上記のルールとルール名のセクションで説明したように、同じ名前で異なるパターンで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 オブジェクト内に 2 つのフィールドがある一致の認識を拒否します。実際には、これはほぼ期待どおりの動作をすることを意味します。
サポートクラスcom.amazon.fsm.ruler.RuleCompiler
があります。これには、JSON ルール定義を受け入れ、null の場合、ルールが構文的に有効であることを意味する String 値を返すcheck()
という名前のメソッドが含まれています。戻り値が Null 以外の場合、問題を説明する人間が判読できるエラー メッセージが含まれます。
便宜上、このメソッドには、 check()
と同じように動作する、 compile()
というメソッドも含まれていますが、IOException をスローしてエラーを通知し、成功すると、Machine のaddRule()
の形式でMap
を返します。 addRule()
メソッドは期待しています。 Machine クラスはこれを内部で使用するため、このメソッドは時間を節約できる可能性があります。
Ruler がキーをコンパイルするとき、結合文字としてドット ( .
) が使用されます。これは、次の 2 つのルールを同じ内部表現にコンパイルすることを意味します。
## has no dots in keys
{ "detail" : { "state" : { "status" : [ "running" ] } } }
## has dots in keys
{ "detail" : { "state.status" : [ "running" ] } }
また、これらのルールは次の 2 つのイベントと一致することも意味します。
## has no dots in keys
{ "detail" : { "state" : { "status" : "running" } } }
## has dots in keys
{ "detail" : { "state.status" : "running" } }
この動作は将来のバージョンで変更される可能性があるため (混乱を避けるため)、信頼しないでください。
複数のルールをマシンにコンパイルし、JSON 文字列として提供されたイベントを照合することで、ルーラーのパフォーマンスを測定します。
平均サイズ約 900 バイトの 213,068 個の JSON イベントを、完全一致、前方一致、接尾辞一致、大文字と小文字の一致、大文字と小文字の一致無視、ワイルドカード一致、数値一致、および一致以外の各 5 つのイベントに対して処理するベンチマークルールを適用して一致をカウントすると、2019 MacBook では次の結果が得られます。
イベントは、次の場合を除き、220K/秒を超える速度で処理されます。
ルールとイベントの処理に関するいくつかの提案を次に示します。
パフォーマンスを考慮すると、Ruler は以下の項目に敏感であるため、イベントとルールのスキーマを設計する際には、次のような提案があります。
詳細については、「貢献」を参照してください。
このプロジェクトは、Apache-2.0 ライセンスに基づいてライセンスされています。詳細については、「ライセンス」を参照してください。