Статьи

Kogito, ergo Rules: от знаний к обслуживанию, без усилий

Добро пожаловать в очередной эпизод этой серии блогов, посвященный инициативе Kogito и нашим усилиям по выводу Drools в облако. Цель этих сообщений — собрать отзывы пользователей о функциях, которые мы предоставляем Kogito.

В этом посте мы представляем два новых способа реализации полного интеллектуального сервиса:

  1. автономные службы правил
  2. интегрированные интеллектуальные рабочие процессы с задачами правил

Единицы Казни в Когито

Как вы, возможно, уже знаете, в Kogito мы делаем переднюю и центральную новую концепцию Unit .

«Единица исполнения» — это термин, который мы используем для обозначения исполняемой части знаний. Единица может быть процессом, набором правил, решением и т. Д.… В случае набора правил мы называем это блоком правил . Если вы решите использовать юниты, в Kogito мы позаботимся обо всем шаблоне, который необходим для автоматической генерации конечной точки REST .

Единица правила состоит в основном из

1) определение данных;

2) набор правил и запросов, которые реализуют поведение модуля (правила механизма правил);

3) опционально, слушатели событий могут быть подключены для ряда целей.

В этом посте мы сосредоточимся на определениях данных, правилах и запросах.

Определения данных задаются объявлением класса Java, который может содержать источники данных. Каждый источник данных представляет собой раздел рабочей памяти, с которым ваши правила будут соответствовать шаблону или вставлять.

Например, предположим, что вы хотите объявить службу оповещений, которая получает события и выдает оповещения в зависимости от некоторых условий. Мы заявляем
Объекты Event и Alert :

01
02
03
04
05
06
07
08
09
10
11
12
package com.acme;
public class Event {
   String type;
   int value;
   // getters and setters
}
 
public class Alert {
  String severity;
  String message;
  // getters and setters
}

Объявление типа модуля AlertingService — это класс, который реализует интерфейс RuleUnitData .

1
2
3
4
5
6
package com.acme;
public class AlertingService implements RuleUnitData {
   private final DataStream<Event> eventData = DataSource.createStream();
   private final DataStream<Alert> alertData = DataSource.createStream();
   // getters and setters
}

Правила определяются в файлах DRL как обычно, за исключением того, что теперь вы должны указать их единицы в верхней части файла. Например, вы можете объявить определение данных для AlertingService следующим образом:

1
2
3
4
5
6
7
8
9
package com.acme;
unit AlertingService;
rule IncomingEvent when
   // matches when a temperature higher than 30 °C is registered (OOPath syntax)
   $e : /eventData [ type == "temperature", value >= 30 ]
then
   System.out.println("incoming event: "+ $e.getMessage());
   alertData.append( new Alert( "warning", "Temperature is too high" ) );
end

Как видите, правила могут совпадать или вставляться в данные источники данных.

Запросы определяются в файлах DRL, как правила, и также принадлежат модулю. Если вы объявите хотя бы один запрос, вы получите конечную точку REST, автоматически сгенерированную бесплатно . Например:

1
2
3
query Warnings
   alerts: /alertData [ severity == "warning" ]
end

сгенерирует конечную точку REST /warnings которые вы сможете вызвать, отправив ей сообщение POST, следующим образом:

1
2
3
4
5
$ curl -X POST \
           -H 'Accept: application/json' \
           -H 'Content-Type: application/json' \
           -d '{ "eventData": [ { "type": "temperature", "value" : 40 } ] }' \
           http://localhost:8080/warnings

Это сгенерирует ответ:

1
[ { "severity": "warning", "message" : "Temperature is too high" } ]

Определение данных на основе Java очень знакомо программистам, но, исходя из ранних отзывов пользователей, мы решили предоставить два альтернативных метода для объявления модуля правила . Мы публикуем эту запись в блоге, чтобы собрать больше отзывов пользователей!

Тип декларации

Объявление типа — это функция DRL для объявления совместимых с Java типов, не зависящих от Java. В серии 7 пользователи могут объявлять типы с синтаксисом:

01
02
03
04
05
06
07
08
09
10
11
package com.acme;
 
declare Event
   type:  String
   value: int
end
 
declare Alert
  severity: String
  message:  String
end

Это делает DRL полностью автономным: все сущности и правила могут быть определены с использованием DRL. Однако у них мало ограничений; например, они не поддерживают реализацию интерфейсов и не поддерживают поля универсального типа. Другими словами, следующее объявление в серии 7 является синтаксически недействительным:

1
2
3
4
5
package com.acme;
declare AlertingService extends RuleUnitData
   eventData: DataStream<Event>
   alertData: DataStream<Alert>
end

В версии 0.8.0 мы снимаем эти ограничения: мы разрешаем ограниченное наследование для интерфейсов (пока разрешено только одно) и объявление универсального типа для полей. Благодаря этим новым функциям следующий фрагмент кода становится действительным DRL.

Короче говоря: теперь вы можете объявить полный микросервис
с одного ДХО .

Загрузите ваш сервис Kogito с помощью архетипа:

1
2
3
4
5
6
mvn archetype:generate \
         -DarchetypeGroupId=org.kie.kogito \
         -DarchetypeArtifactId=kogito-quarkus-archetype \
         -DarchetypeVersion=0.8.0 \
         -DgroupId=com.acme \
         -DartifactId=sample-kogito

На данный момент ни одна версия Quarkus не входит в комплект Kogito 0.8.0; в противном случае вы могли бы использовать mvn io.quarkus:quarkus-maven-plugin:create .

Теперь очистите содержимое src/main и затем поместите этот DRL в папку src/main/resources/com/acme :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.acme;
unit AlertingService;
 
import org.kie.kogito.rules.DataStream;
import org.kie.kogito.rules.RuleUnitData;
 
declare Event
   type:  String
   value: int
end
 
declare Alert
  severity: String
  message:  String
end
 
declare AlertingService extends RuleUnitData
   eventData: DataStream<Event>
   alertData: DataStream<Alert>
end
 
rule IncomingEvent when
   // matches when a temperature higher than 30 °C is registered (OOPath syntax)
   $e : /eventData [ type == "temperature", value >= 30 ]
then
   System.out.println("incoming event: "+ $e.getMessage());
   alertData.append( new Alert( "warning", "Temperature is too high: " + $e ) );
end
 
query Warnings
   alerts: /alertData [ severity == "warning" ]
end

Теперь запустите сервис Quarkus в режиме разработчика с помощью:

1
$ mvn compile quarkus:dev

Вот и все, теперь вы готовы curl свой сервис:

1
2
3
4
5
$ curl -X POST \
           -H 'Accept: application/json' \
           -H 'Content-Type: application/json' \
           -d '{ "eventData": [ { "type": "temperature", "value" : 40 } ] }' \
           http://localhost:8080/warnings

Интеграция рабочего процесса

Еще один способ предоставления службы на основе правил — через рабочий процесс .

Рабочий процесс (иногда называемый «бизнес-процессом») описывает последовательность шагов на диаграмме и обычно объявляет переменные : держатели данных для значений, которыми манипулируют при выполнении. Тип данных одной такой переменной может быть любым : вы можете использовать классы Java, но в этом примере мы снова будем использовать наши объявленные типы данных.

01
02
03
04
05
06
07
08
09
10
11
package com.acme;
 
declare Event
   type:  String
   value: int
end
 
declare Alert
  severity: String
  message:  String
end

Давайте вызовем этот рабочий процесс com.acme.AlertingWorkflow и объявим переменные eventData и alertData :

Рабочий процесс, который включает в себя задачу правила, может вообще пропустить объявление данных модуля правила: в этом случае модуль правила выводится непосредственно из структуры процесса: каждая переменная будет вставлена ​​в источник данных с тем же именем .

Имя модуля объявляется процессом с использованием синтаксического unit:com.acme.AlertingService . Вы по-прежнему можете явно объявить модуль com.acme.AlertingService ; в этом случае процесс подберет декларацию, которую вы написали вручную.

Примечание. Возможно, вы заметили, что мы используем поле «Группа правил потока». Мы будем реализовывать более явную поддержку в пользовательском интерфейсе в будущем.

Загрузите ваш сервис Kogito с помощью архетипа:

1
2
3
4
5
6
mvn archetype:generate \
         -DarchetypeGroupId=org.kie.kogito \
         -DarchetypeArtifactId=kogito-quarkus-archetype \
         -DarchetypeVersion=0.8.0 \
         -DgroupId=com.acme \
         -DartifactId=sample-kogito

Будьте осторожны Поддержка этой функции является экспериментальной, поэтому она может не работать без проблем с горячей перезагрузкой кода Quarkus; нам также понадобится следующий дополнительный шаг, чтобы включить его, но это изменится в будущем.

Обновите ваш pom.xml следующим объявлением плагина:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<build>
    <plugins>
      <plugin>
        <groupId>org.kie.kogito</groupId>
        <artifactId>kogito-maven-plugin</artifactId>
        <version>0.8.0</version>
        <executions>
          <execution>
            <goals>
              <goal>generateDeclaredTypes</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
   </build>

Теперь вы можете очистить содержимое src/main , а затем отбросить процесс и следующий DRL в папку src/main/resources/com/acme .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.acme;
unit AlertingService;
 
import org.kie.kogito.rules.DataStream;
import org.kie.kogito.rules.RuleUnitData;
 
declare Event
   type:  String
   value: int
end
 
declare Alert
  severity: String
  message:  String
end
 
rule IncomingEvent when
   // matches when a temperature higher than 30 °C is registered (OOPath syntax)
   $e : /eventData [ type == "temperature", value >= 30 ]
then
   System.out.println("incoming event: "+ $e.getMessage());
   alertData.set( new Alert( "warning""Temperature is too high: " + $e ) );
end

Как вы могли заметить, вы не обязаны объявлять запрос явно: процесс будет отображать содержимое переменных в качестве ответа; он сгенерирует конечную точку /AlertingWorkflow и примет запрос POST следующей формы:

1
2
3
4
5
$ curl -X POST \
           -H 'Accept: application/json' \
           -H 'Content-Type: application/json' \
           -d '{ "eventData": { "type": "temperature", "value" : 40 } }' \
           http://localhost:8080/AlertingWorkflow

Ответ будет:

01
02
03
04
05
06
07
08
09
10
11
{
  "id": ...,
  "eventData": {
    "type": "temperature",
    "value": 100
  },
  "alertData": {
    "severity": "warning",
    "message": "Temperature is too high: Event( type=temperature, value=100 )"
  }
}

Однако, если вы объявите запрос, будет также доступна отдельная конечная точка. Например, если вы объявите запрос Warnings вы все равно сможете отправить POST по http://localhost:8080/warnings и отдельно запустить службу правил следующим образом:

1
2
3
4
5
$ curl -X POST \
       -H 'Accept: application/json' \
       -H 'Content-Type: application/json' \
       -d '{ "eventData": { "type": "temperature", "value" : 40 } }' \
       http://localhost:8080/warnings

Обратите внимание, что запрос больше не содержит список событий. Это потому, что переменные процесса отображаются в отдельные значения вместо DataStreams.

Вывод

Мы немного ознакомились с работой, которую мы делаем, чтобы улучшить начальный опыт работы с правилами и процессами в Kogito. С этими изменениями мы надеемся предоставить более упорядоченный способ определения услуг, основанных на знаниях. Разработчики всегда смогут быть более откровенными в отношении данных, которые они хотят обработать, включив написание Java; но если они хотят, они могут использовать полностью рабочий процесс разработки, ориентированный на DSL.

Для ленивых, примеры доступны на https://github.com/evacchi/kogito-rules-example/tree/master/code Удачи!

Смотреть оригинальную статью здесь: Kogito, ergo Rules: от знаний к сервису, без усилий

Мнения, высказанные участниками Java Code Geeks, являются их собственными.