Event Ruler (disebut Ruler di dokumen lainnya agar singkatnya) adalah pustaka Java yang memungkinkan pencocokan Rules dengan Events . Peristiwa adalah daftar bidang, yang dapat diberikan sebagai pasangan nama/nilai atau sebagai objek JSON. Aturan mengaitkan nama bidang peristiwa dengan daftar nilai yang mungkin. Ada dua alasan untuk menggunakan Penggaris:
Isi:
Cara termudah untuk menjelaskannya adalah dengan memberi contoh.
Peristiwa adalah objek JSON. Berikut ini contohnya:
{
"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"
}
}
Anda juga dapat melihatnya sebagai kumpulan pasangan nama/nilai. Untuk singkatnya, kami hanya menyajikan contohnya saja. Ruler memiliki API untuk menyediakan acara baik dalam bentuk JSON dan sebagai pasangan nama/nilai:
+--------------+------------------------------------------+
| name | value |
|--------------|------------------------------------------|
| source | "aws.ec2" |
| detail-type | "EC2 Instance State-change Notification" |
| detail.state | "running" |
+--------------+------------------------------------------+
Peristiwa dalam bentuk JSON dapat disediakan dalam bentuk String JSON mentah, atau Jackson JsonNode yang diurai.
Aturan di bagian ini semuanya cocok dengan contoh kejadian di atas:
{
"detail-type" : [ "EC2 Instance State-change Notification" ] ,
"resources" : [ "arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000" ] ,
"detail" : {
"state" : [ "initializing" , "running" ]
}
}
Ini akan mencocokkan peristiwa apa pun dengan nilai yang diberikan untuk nilai resource
, detail-type
, dan detail.state
, dengan mengabaikan bidang lain dalam peristiwa tersebut. Ini juga akan cocok jika nilai detail.state
telah "initializing"
.
Nilai dalam aturan selalu diberikan dalam bentuk array, dan cocok jika nilai dalam kejadian adalah salah satu nilai yang disediakan dalam array. Referensi ke resources
menunjukkan bahwa jika nilai dalam peristiwa juga berupa larik, aturan akan cocok jika perpotongan antara larik peristiwa dan larik aturan tidak kosong.
{
"time" : [ { "prefix" : "2017-10-02" } ]
}
Pencocokan awalan hanya berfungsi pada bidang bernilai string.
{
"source" : [ { "prefix" : { "equals-ignore-case" : "EC2" } } ]
}
Pencocokan awalan sama dengan abaikan huruf besar/kecil hanya berfungsi pada bidang bernilai string.
{
"source" : [ { "suffix" : "ec2" } ]
}
Pencocokan sufiks hanya berfungsi pada bidang bernilai string.
{
"source" : [ { "suffix" : { "equals-ignore-case" : "EC2" } } ]
}
Pencocokan sufiks sama dengan abaikan huruf besar/kecil hanya berfungsi pada bidang bernilai string.
{
"source" : [ { "equals-ignore-case" : "EC2" } ]
}
Pencocokan huruf besar-kecil yang sama hanya berfungsi pada bidang bernilai string.
{
"source" : [ { "wildcard" : "Simple*Service" } ]
}
Pencocokan karakter pengganti hanya berfungsi pada bidang bernilai string. Nilai tunggal dapat berisi nol hingga banyak karakter wildcard, namun karakter wildcard berturut-turut tidak diperbolehkan. Untuk mencocokkan karakter asterisk secara spesifik, karakter wildcard dapat di-escape dengan garis miring terbalik. Dua garis miring terbalik yang berurutan (yaitu garis miring terbalik yang diloloskan dengan garis miring terbalik) mewakili karakter garis miring terbalik yang sebenarnya. Garis miring terbalik yang keluar dari karakter apa pun selain tanda bintang atau garis miring terbalik tidak diperbolehkan.
Apa pun kecuali pencocokan akan melakukan sesuai dengan namanya: mencocokkan apa pun kecuali yang ditentukan dalam aturan.
Apa pun-kecuali berfungsi dengan string tunggal dan nilai atau daftar numerik, yang harus berisi seluruh string atau seluruh numerik. Ini juga dapat diterapkan pada awalan, akhiran, atau pencocokan huruf besar-kecil yang sama dengan sebuah string atau daftar string.
Tunggal apa pun kecuali (string, lalu numerik):
{
"detail" : {
"state" : [ { "anything-but" : "initializing" } ]
}
}
{
"detail" : {
"x-limit" : [ { "anything-but" : 123 } ]
}
}
Apa pun kecuali daftar (string):
{
"detail" : {
"state" : [ { "anything-but" : [ "stopped" , "overloaded" ] } ]
}
}
Apa pun kecuali daftar (angka):
{
"detail" : {
"x-limit" : [ { "anything-but" : [ 100 , 200 , 300 ] } ]
}
}
Apa pun kecuali awalan:
{
"detail" : {
"state" : [ { "anything-but" : { "prefix" : "init" } } ]
}
}
Apa pun kecuali daftar awalan (string):
{
"detail" : {
"state" : [ { "anything-but" : { "prefix" : [ "init" , "error" ] } } ]
}
}
Apa pun kecuali akhiran:
{
"detail" : {
"instance-id" : [ { "anything-but" : { "suffix" : "1234" } } ]
}
}
Apa pun kecuali daftar sufiks (string):
{
"detail" : {
"instance-id" : [ { "anything-but" : { "suffix" : [ "1234" , "6789" ] } } ]
}
}
Apa pun-kecuali-abaikan-kasus:
{
"detail" : {
"state" : [ { "anything-but" : { "equals-ignore-case" : "Stopped" } } ]
}
}
Daftar kasus apa pun kecuali abaikan (string):
{
"detail" : {
"state" : [ { "anything-but" : { "equals-ignore-case" : [ "Stopped" , "OverLoaded" ] } } ]
}
}
Apa pun kecuali wildcard:
{
"detail" : {
"state" : [ { "anything-but" : { "wildcard" : "*/bin/*.jar" } } ]
}
}
Apa pun kecuali daftar wildcard (string):
{
"detail" : {
"state" : [ { "anything-but" : { "wildcard" : [ "*/bin/*.jar" , "*/bin/*.class" ] } } ]
}
}
{
"detail" : {
"c-count" : [ { "numeric" : [ ">" , 0 , "<=" , 5 ] } ] ,
"d-count" : [ { "numeric" : [ "<" , 10 ] } ] ,
"x-limit" : [ { "numeric" : [ "=" , 3.018e2 ] } ]
}
}
Di atas, referensi ke c-count
, d-count
, dan x-limit
menggambarkan pencocokan numerik, dan hanya berfungsi dengan nilai berupa angka JSON. Pencocokan numerik mendukung presisi dan jangkauan yang sama dengan primitif double
Java yang mengimplementasikan standar IEEE 754 binary64
.
{
"detail" : {
"source-ip" : [ { "cidr" : "10.0.0.0/24" } ]
}
}
Ini juga berfungsi dengan alamat IPv6.
Pencocokan yang ada berfungsi pada ada atau tidaknya bidang dalam acara JSON.
Aturan di bawah ini akan cocok dengan peristiwa apa pun yang memiliki bidang detail.c-count.
{
"detail" : {
"c-count" : [ { "exists" : true } ]
}
}
Aturan di bawah ini akan cocok dengan peristiwa apa pun yang tidak memiliki kolom detail.c-count.
{
"detail" : {
"c-count" : [ { "exists" : false } ]
}
}
Catatan Kecocokan Exists
hanya berfungsi pada simpul daun. Ini tidak berfungsi pada node perantara.
Sebagai contoh, contoh di atas untuk exists : false
akan cocok dengan kejadian di bawah ini:
{
"detail-type" : [ "EC2 Instance State-change Notification" ] ,
"resources" : [ "arn:aws:ec2:us-east-1:123456789012:instance/i-000000aaaaaa00000" ] ,
"detail" : {
"state" : [ "initializing" , "running" ]
}
}
tetapi juga akan cocok dengan kejadian di bawah ini karena c-count
bukan simpul daun:
{
"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" } ]
}
}
Seperti yang ditunjukkan oleh contoh di atas, Ruler menganggap aturan cocok jika semua bidang yang disebutkan dalam aturan cocok, dan menganggap bidang cocok jika salah satu nilai bidang yang diberikan cocok, artinya Ruler telah menerapkan logika "Dan" ke semua bidang secara default tanpa primitif "Dan" diperlukan .
Ada dua cara untuk mencapai efek "Atau":
Primitif "$or" yang memungkinkan pelanggan secara langsung mendeskripsikan hubungan "Atau" antar bidang dalam aturan.
Ruler mengakui hubungan "Atau" hanya jika rule telah memenuhi seluruh kondisi di bawah ini:
/src/main/software/amazon/event/ruler/Constants.java#L38
misalnya, aturan di bawah ini tidak akan diuraikan sebagai " Atau hubungan "karena" numerik "dan" awalan "adalah kata kunci yang dilindungi undang-undang. {
"$or": [ {"numeric" : 123}, {"prefix": "abc"} ]
}
Jika tidak, Ruler hanya memperlakukan "$or" sebagai nama file biasa, sama seperti string lain dalam aturan.
Biasa "Atau":
// Effect of "source" && ("metricName" || "namespace")
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{ "namespace" : [ "AWS/EC2" , "AWS/ES" ] }
]
}
Paralel "Atau":
// 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" ] }
]
}
}
"Atau" memiliki "Dan" di dalamnya
// Effect of ("source" && ("metricName" || ("metricType && "namespace") || "scope"))
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : [
{ "metricName" : [ "CPUUtilization" , "ReadLatency" ] } ,
{
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
} ,
{ "scope" : [ "Service" ] }
]
}
Bersarang "Atau" dan "Dan"
// 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" mungkin sudah digunakan sebagai kunci normal di beberapa aplikasi (walaupun kemungkinannya jarang). Untuk kasus ini, Ruler mencoba yang terbaik untuk menjaga kompatibilitas ke belakang. Hanya jika 3 kondisi yang disebutkan di atas terpenuhi, perilaku penggaris akan berubah karena aturan tersebut mengasumsikan bahwa aturan Anda benar-benar menginginkan OR dan salah dikonfigurasi hingga saat ini. Misalnya, aturan di bawah ini akan tetap berfungsi sebagai aturan normal dengan memperlakukan "$or" sebagai nama bidang normal dalam aturan dan kejadian:
{
"source" : [ "aws.cloudwatch" ] ,
"$or" : {
"metricType" : [ "MetricType" ] ,
"namespace" : [ "AWS/EC2" , "AWS/ES" ]
}
}
Lihat /src/test/data/normalRulesWithOrWording.json
untuk contoh lebih lanjut bahwa "$or" diurai sebagai nama bidang normal oleh Ruler.
Kata kunci "$or" sebagai primitif hubungan "Atau" tidak boleh dirancang sebagai bidang normal di Peristiwa dan Aturan. Ruler mendukung aturan lama di mana "$or" diurai sebagai nama bidang normal untuk menjaga kompatibilitas ke belakang dan memberikan waktu bagi tim untuk memigrasikan penggunaan "$or" lama mereka dari acara dan aturan mereka sebagai nama file normal. Campuran penggunaan "$or" sebagai "Or" primitif, dan "$or" sebagai nama bidang normal tidak didukung dengan sengaja oleh Ruler untuk menghindari terjadinya ambiguitas yang sangat canggung pada "$or".
Ada dua cara untuk menggunakan Penggaris. Anda dapat mengompilasi beberapa aturan ke dalam "Mesin", lalu menggunakan salah satu metode rulesForEvent()
atau metode rulesForJSONEvent()
untuk memeriksa aturan mana yang cocok dengan Peristiwa apa pun. Perbedaan antara kedua metode ini dibahas di bawah. Diskusi ini akan menggunakan rulesForEvent()
secara umum kecuali jika perbedaannya penting.
Alternatifnya, Anda dapat menggunakan metode boolean statis tunggal untuk menentukan apakah suatu peristiwa cocok dengan aturan tertentu.
Ada satu metode boolean statis Ruler.matchesRule(event, rule)
- kedua argumen disediakan sebagai string JSON.
CATATAN: Ada metode lain yang tidak digunakan lagi yang disebut Ruler.matches(event, rule)
yang tidak boleh digunakan karena hasilnya tidak sesuai dengan rulesForJSONEvent()
dan rulesForEvent()
. Lihat dokumentasi di Ruler.matches(event, rule)
untuk detailnya.
Waktu pencocokan tidak bergantung pada jumlah aturan. Ini adalah pilihan terbaik jika Anda memiliki beberapa kemungkinan aturan yang ingin Anda pilih, dan terutama jika Anda memiliki cara untuk menyimpan Mesin yang dikompilasi.
Waktu pencocokan dipengaruhi oleh tingkat non-determinisme yang disebabkan oleh aturan wildcard dan apa pun kecuali aturan wildcard. Performa menurun karena semakin banyak awalan aturan wildcard yang cocok dengan kejadian terburuk teoretis. Untuk menghindari hal ini, aturan wildcard yang berkaitan dengan bidang peristiwa yang sama harus menghindari awalan umum yang mengarah ke karakter wildcard pertamanya. Jika awalan umum diperlukan, gunakan jumlah minimum karakter wildcard dan batasi urutan karakter berulang yang muncul setelah karakter wildcard. MachineComplexityEvaluator dapat digunakan untuk mengevaluasi mesin dan menentukan tingkat non-determinisme, atau "kompleksitas" (yaitu berapa banyak awalan aturan wildcard yang cocok dengan kejadian terburuk teoretis). Berikut adalah beberapa titik data yang menunjukkan penurunan kinerja secara umum untuk meningkatkan skor kompleksitas.
Penting untuk membatasi kompleksitas mesin untuk melindungi aplikasi Anda. Setidaknya ada dua strategi berbeda untuk membatasi kompleksitas mesin. Mana yang lebih masuk akal mungkin bergantung pada aplikasi Anda.
Strategi #1 lebih ideal karena mengukur kompleksitas sebenarnya dari mesin yang memuat semua aturan. Jika memungkinkan, strategi ini harus digunakan. Kelemahannya adalah, katakanlah Anda memiliki bidang kendali yang memungkinkan pembuatan satu aturan dalam satu waktu, hingga jumlah yang sangat besar. Kemudian untuk setiap operasi bidang kontrol ini, Anda harus memuat semua aturan yang ada untuk melakukan validasi. Ini bisa jadi sangat mahal. Ini juga rentan terhadap kondisi balapan. Strategi #2 adalah kompromi. Ambang batas yang digunakan oleh strategi #2 akan lebih rendah dari strategi #1 karena ini merupakan ambang batas per aturan. Katakanlah Anda ingin kompleksitas mesin, dengan semua aturan ditambahkan, tidak lebih dari 300. Kemudian dengan strategi #2, misalnya, Anda dapat membatasi setiap mesin aturan tunggal hingga kompleksitas 10, dan mengizinkan 30 aturan yang berisi pola wildcard . Dalam kasus terburuk absolut di mana kompleksitas bersifat aditif sempurna (tidak mungkin terjadi), hal ini akan menghasilkan mesin dengan kompleksitas 300. Kelemahannya adalah kecil kemungkinan kompleksitas tersebut akan menjadi aditif sempurna, sehingga jumlah aturan yang mengandung wildcard akan bertambah. kemungkinan besar akan dibatasi jika tidak diperlukan.
Untuk strategi #2, bergantung pada bagaimana aturan disimpan, atribut tambahan mungkin perlu ditambahkan ke aturan untuk menunjukkan mana yang nondeterministik (yaitu berisi pola wildcard) untuk membatasi jumlah aturan yang mengandung wildcard.
Berikut ini cuplikan kode yang mengilustrasikan cara membatasi kompleksitas pola tertentu, seperti strategi #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 ;
}
}
Kelas utama tempat Anda akan berinteraksi mengimplementasikan pencocokan aturan berbasis mesin negara. Metode yang menarik adalah:
addRule()
- menambahkan aturan baru ke mesindeleteRule()
- menghapus aturan dari mesinrulesForEvent()
/ rulesForJSONEvent()
- menemukan aturan di mesin yang cocok dengan suatu peristiwa Ada dua varian: Machine
dan GenericMachine<T>
. Mesin hanyalah GenericMachine<String>
. API mengacu pada tipe generik sebagai "nama", yang mencerminkan sejarah: Versi String dibuat pertama kali dan string yang disimpan dan dikembalikan dianggap sebagai nama aturan.
Demi keamanan, tipe yang digunakan untuk "memberi nama" aturan harus tidak dapat diubah. Jika Anda mengubah konten objek saat sedang digunakan sebagai nama aturan, hal ini dapat merusak pengoperasian Ruler.
Konstruktor GenericMachine dan Mesin secara opsional menerima objek GenericMachineConfiguration, yang memperlihatkan opsi konfigurasi berikut.
Default: false Biasanya, Status Nama digunakan kembali untuk rangkaian dan pola kunci tertentu jika rangkaian dan pola kunci ini telah ditambahkan sebelumnya, atau jika pola telah ditambahkan untuk rangkaian kunci tertentu. Oleh karena itu, secara default, penggunaan kembali NameState bersifat oportunistik. Namun dengan menyetel tanda ini ke true, penggunaan kembali NameState akan dipaksakan untuk rangkaian kunci berikutnya. Artinya, pola pertama yang ditambahkan untuk rangkaian kunci akan menggunakan kembali NameState jika rangkaian kunci tersebut telah ditambahkan sebelumnya. Artinya setiap rangkaian kunci memiliki satu NameState. Hal ini meningkatkan pemanfaatan memori secara eksponensial dalam beberapa kasus, namun menyebabkan lebih banyak sub-aturan yang disimpan di NameStates individual, yang terkadang diulangi oleh Ruler, yang dapat menyebabkan regresi kinerja waktu proses yang sederhana. Setelan bawaannya adalah false untuk kompatibilitas mundur, namun kemungkinan besar, semua aplikasi kecuali aplikasi yang paling sensitif terhadap latensi akan mendapat manfaat jika menyetelnya ke true.
Berikut ini contoh sederhananya. Mempertimbangkan:
machine . addRule ( "0" , "{"key1": ["a", "b", "c"]}" ) ;
Pola "a" menciptakan NameState, dan kemudian, bahkan dengan tambahanNameStateReuse=false, pola kedua ("b") dan pola ketiga ("c") menggunakan kembali NameState yang sama. Namun pertimbangkan hal berikut ini:
machine . addRule ( "0" , "{"key1": ["a"]}" ) ;
machine . addRule ( "1" , "{"key1": ["b"]}" ) ;
machine . addRule ( "2" , "{"key1": ["c"]}" ) ;
Sekarang, dengan tambahanNameStateReuse=false, kita mendapatkan tiga NameStates, karena pola pertama yang ditemukan untuk rangkaian kunci pada setiap penambahan aturan akan membuat NameState baru. Jadi, "a", "b", dan "c" semuanya mendapatkan Status Namanya sendiri. Namun, dengan tambahanNameStateReuse=true, "a" akan membuat NameState baru, lalu "b" dan "c" akan menggunakan kembali NameState yang sama. Hal ini dicapai dengan menyimpan bahwa kita sudah memiliki NameState untuk kunci berikutnya "key1".
Perhatikan bahwa tidak masalah jika setiap addRule menggunakan nama aturan yang berbeda atau nama aturan yang sama.
Semua bentuk metode ini memiliki argumen pertama yang sama, sebuah String yang memberikan nama Aturan dan dikembalikan oleh rulesForEvent()
. Argumen lainnya memberikan pasangan nama/nilai. Mereka dapat disediakan dalam JSON seperti pada contoh di atas (melalui String, Reader, InputStream, atau byte[]
), atau sebagai Map<String, List<String>>
, dengan kuncinya adalah nama bidang dan nilai adalah daftar kemungkinan kecocokan; menggunakan contoh di atas, akan ada kunci bernama detail.state
yang nilainya akan berupa daftar yang berisi "initializing"
dan "running"
.
Catatan: Metode ini (dan juga deleteRule()
) disinkronkan, jadi hanya satu thread yang dapat memperbarui mesin kapan saja.