Статьи

Kogito, ergo Rules — Часть 2. Всеобъемлющая модель исполнения для правил

Это второй пост из серии обновлений об инициативе Kogito и наших усилиях по переносу Drools в облако. В этом посте мы углубимся в детали блоков правил и покажем, почему мы рады этим.

Всеобъемлющая модель исполнения для правил

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

Блок правил собирает набор правил вместе с описанием рабочей памяти, на которую действуют эти правила. Описание рабочей памяти написано как обычный класс Java с полями DataSource . Каждый источник данных представляет собой типизированный раздел рабочей памяти, и существуют разные типы источников данных с различными функциями. Например, в следующем примере мы использовали источник данных только для добавления, называемый
поток данных.

1
2
3
4
public class MonitoringService implements RuleUnitMemory {
   private final DataStream<Event> events = DataSource.createStream();
   private final DataStream<Alert> alerts = DataSource.createStream();
}

Правила данного блока правил собраны в файлах DRL с объявлением модуля

1
2
3
4
5
6
7
8
9
package org.kie.kogito.rules.alerting
unit MonitoringService
rule IncomingEvent when
   // matches when a temperature higher than 30 °C is registered (OOPath syntax)
   $e : /events # Temperature[ value >= 30 ] // Temperature is an Event subclass
then
   System.out.println("incoming event: "+ $e.getMessage());
   alerts.append( new WarningHighTemperature($e) );
end

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

Блок правил передается на выполнение в планировщик . Подразделения правил могут принять решение о передаче их исполнения другим подразделениям правил, эффективно вводя их в действие. Например:

1
2
3
4
5
6
7
rule IncomingEvent when
   // matches when a temperature higher than 30 °C is registered (OOPath syntax)
   $e : /events # Temperature[ value >= 30 ] // Temperature is an Event subclass
then
  // Suspend execution of this unit, yield to the HighTemperatureUnit
  HighTemperatureUnit.createInstance(events).run();
end

Но единицы правил могут быть также переведены в длительное состояние . В таком случае,
другие блоки правил могут быть запущены одновременно ; поскольку источники данных могут совместно использоваться единицами, единицы могут координироваться путем обмена сообщениями.

Рассмотрим следующий пример:

1
2
3
4
5
6
7
8
9
package org.kie.kogito.rules.alerting
unit MonitoringService
rule IncomingEvent when
   // matches when a temperature higher than 30 °C is registered (OOPath syntax)
   $e : /events # Temperature[ value >= 30 ] // Temperature is an Event subclass
then
   System.out.println("incoming event: "+ $e.getMessage());
   alerts.append( new WarningHighTemperature($e) );
end

Определенным образом единицы правил ведут себя как «актеры», обменивающиеся сообщениями . Однако весьма характерным образом блоки правил допускают гораздо более сложные цепочки исполнений, которые соответствуют рассуждениям на основе правил. Например,
рассмотрим этот пример из руководства Акки :

1
2
3
4
5
6
7
8
9
override def receive: Receive = {
    case RecordTemperature(id, value) =>
      log.info("Recorded temperature reading {} with {}", value, id)
      lastTemperatureReading = Some(value)
      sender() ! TemperatureRecorded(id)
 
    case ReadTemperature(id) =>
      sender() ! RespondTemperature(id, lastTemperatureReading)
  }

Как видите, совпадения с образцами в Akka строго по отдельным сообщениям . Это неудивительно, потому что актеры обрабатывают одно сообщение за раз. В механизме правил нам разрешено писать несколько правил, реагирующих на все состояние рабочей памяти во время выполнения: это значительно отличается от дизайна чисто актерской модели, но в то же время дает большую гибкость в как вы можете написать бизнес-логику вашего приложения.

Источники данных

Стоит потратить несколько слов и на источники данных. Конструкция источника данных может рассматриваться как раздел и абстракция над традиционной рабочей памятью. Будут доступны различные типы источников данных: полнофункциональные хранилища данных могут поддерживать добавление, удаление и обновление значений, что позволяет выполнять более традиционные операции над рабочей памятью; в то время как более ограниченные потоки данных только для присоединения было бы легче интегрировать с внешними источниками данных и приемниками данных, такими как соединители Camel; такие ограничения также были бы полезны для обеспечения более сложных вариантов использования, таких как параллельное, поточно-ориентированное выполнение и
постоянный общий канал (например, Kafka) между узлами кластера OpenShift , реализующий полностью распределенный механизм правил.

Kogito: Ergo Cloud

Параллельные и распределенные сценарии использования интригуют , но мы должны сделать это с помощью маленьких шагов. Однако это не значит, что первые шаги не будут такими захватывающими по-своему.

Для Когито мы хотим подчеркнуть облачный вариант использования без сохранения состояния, где
поток управления выводится с помощью процессов и, с силой
Quarkus, мы можем скомпилировать это в супер-быстрые собственные двоичные файлы. Вот почему в ближайшие несколько недель мы завершим и выпустим блоки правил для
автоматизированная реализация сервиса REST .

В этом случае типизированное объявление модуля правила на основе Java
автоматически сопоставляется с сигнатурой конечной точки REST. Отправка сообщения в конечную точку подразумевает создание экземпляра модуля, вставку данных в источники данных, правила запуска, возврат полезной нагрузки ответа. Ответ вычисляется с использованием предоставленного пользователем запроса . Например, рассмотрим этот пример:

1
2
3
4
5
package org.kie.kogito.rules.alerting
unit MonitoringService
query Alerts(Alert alert)
   alert := /alerts # Warning // select all sub-type Warning
end

Пользователи могут публиковать события, используя автоматически созданную конечную точку службы мониторинга. Ответ будет результатом запроса. В нашем случае:

1
2
3
4
5
{
  "events": [
    { "type": "WarningHighTemperature", "value": 40, "timestamp": "2019-07-12T18:25:45.000Z" }
  ]
}

ответ будет результатом запроса. В нашем случае:

1
2
3
4
5
6
7
{
  "events": [
    { "type": "Temperature", "value": 10, "timestamp": "2019-07-12T18:25:43.000Z" },
    { "type": "Temperature", "value": 25, "timestamp": "2019-07-12T18:25:44.000Z" },
    { "type": "Temperature", "value": 40, "timestamp": "2019-07-12T18:25:45.000Z" }
  ]
}

Облачно, возможны правила

Мы представили наше видение следующего поколения нашего механизма правил в Когито и за его пределами. Вариант использования без сохранения состояния — это только первый шаг к тому, что, по нашему мнению, будет действительно инновационным подходом к механизмам правил. В следующие месяцы мы будем работать над улучшением поддержки планирования и развертывания модулей параллельно (локально) и распределенно (в Openshift), так что следите за обновлениями. Тем временем мы хотим услышать от вас направление, в котором мы идем.

Смотрите оригинальную статью здесь: Kogito, ergo Rules — Часть 2. Всеобъемлющая модель исполнения для правил

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